Skip to content

Commit 464f5b4

Browse files
committed
refactor xdr strings to be represented soley via bytes with a dx friendly XdrString wrapper class
1 parent 03d7cd3 commit 464f5b4

44 files changed

Lines changed: 802 additions & 505 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/base/memo.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export type MemoType =
4444
| MemoTypeReturn
4545
| MemoTypeText;
4646

47-
export type MemoValue = Buffer | string | null;
47+
export type MemoValue = Buffer | string | null | Uint8Array;
4848

4949
type MemoValueMap = {
5050
[MemoNone]: null;
@@ -235,10 +235,11 @@ export class Memo<T extends MemoType = MemoType> {
235235
/**
236236
* Creates and returns a `MemoText` memo.
237237
*
238-
* @param text - memo text
238+
* @param text - memo text. A JS string is UTF-8 encoded on the wire;
239+
* pass a `Buffer`/`Uint8Array` for byte-exact content.
239240
*/
240-
static text(text: string): Memo<MemoTypeText> {
241-
return new Memo(MemoText, text);
241+
static text(text: string | Buffer | Uint8Array): Memo<MemoTypeText> {
242+
return new Memo(MemoText, text as MemoValue);
242243
}
243244

244245
/**
@@ -305,7 +306,7 @@ export class Memo<T extends MemoType = MemoType> {
305306
case "memoId":
306307
return Memo.id(object.value.toString());
307308
case "memoText":
308-
return Memo.text(object.value as string);
309+
return Memo.text(Buffer.from(object.text.bytes));
309310
case "memoHash":
310311
return Memo.hash(Buffer.from(object.value.toBytes()));
311312
case "memoReturn":

src/base/operation.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ export class Operation {
233233
result.highThreshold = attrs.highThreshold ?? undefined;
234234
// home_domain is checked by iscntrl in stellar-core
235235
result.homeDomain =
236-
attrs.homeDomain === null ? undefined : attrs.homeDomain;
236+
attrs.homeDomain === null ? undefined : attrs.homeDomain.toString();
237237

238238
if (attrs.signer) {
239239
const signer: Record<string, unknown> = {};
@@ -301,7 +301,7 @@ export class Operation {
301301
case "manageData": {
302302
result.type = "manageData";
303303
// manage_data.name is checked by iscntrl in stellar-core
304-
result.name = attrs.dataName;
304+
result.name = attrs.dataName.toString();
305305
result.value =
306306
attrs.dataValue === null
307307
? undefined
@@ -524,7 +524,7 @@ function extractRevokeSponshipDetails(
524524
case "data": {
525525
result.type = "revokeDataSponsorship";
526526
result.account = accountIdtoAddress(ledgerKey.data.accountId);
527-
result.name = ledgerKey.data.dataName;
527+
result.name = ledgerKey.data.dataName.toString();
528528
break;
529529
}
530530
case "claimableBalance": {

src/base/scval.ts

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -425,34 +425,13 @@ export function scValToNative(scv: ScVal): any {
425425
// Note that we assume a utf8 encoding (ascii-compatible). For other
426426
// encodings, you should probably use bytes anyway. If it cannot be decoded,
427427
// the raw bytes are returned.
428-
case "scvSymbol": {
429-
const v = scv.value;
430-
if (
431-
Buffer.isBuffer(v) ||
432-
(ArrayBuffer.isView(v) && typeof v !== "string")
433-
) {
434-
try {
435-
return new TextDecoder().decode(v);
436-
} catch {
437-
return new Uint8Array((v as ArrayBufferView).buffer); // copy of bytes
438-
}
439-
}
440-
return v; // string already
441-
}
442-
case "scvString": {
443-
const v = scv.value;
444-
if (
445-
Buffer.isBuffer(v) ||
446-
(ArrayBuffer.isView(v) && typeof v !== "string")
447-
) {
448-
try {
449-
return new TextDecoder().decode(v);
450-
} catch {
451-
return new Uint8Array((v as ArrayBufferView).buffer); // copy of bytes
452-
}
453-
}
454-
return v; // string already
455-
}
428+
case "scvSymbol":
429+
// Reach the raw XdrString wrapper (the `.value` getter would do a
430+
// strict UTF-8 decode that throws on invalid bytes; we want a best-
431+
// effort decode that falls back to raw bytes for binary content).
432+
return scv.sym.asStringOrBytes();
433+
case "scvString":
434+
return scv.str.asStringOrBytes();
456435

457436
// these can be converted to bigint
458437
case "scvTimepoint":

src/contract/assembled_transaction.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ export class AssembledTransaction<T> {
417417

418418
try {
419419
contractAddress = invokeContractArgs.contractAddress;
420-
functionName = invokeContractArgs.functionName;
420+
functionName = invokeContractArgs.functionName.toString();
421421
} catch {
422422
throw new Error(
423423
"Could not extract contract address or method name from the transaction envelope.",
@@ -463,7 +463,7 @@ export class AssembledTransaction<T> {
463463
options.contractId,
464464
);
465465

466-
const xdrMethod = invokeContractArgs.functionName;
466+
const xdrMethod = invokeContractArgs.functionName.toString();
467467

468468
if (xdrMethod !== options.method) {
469469
throw new Error(
@@ -518,7 +518,7 @@ export class AssembledTransaction<T> {
518518
options.contractId,
519519
);
520520

521-
const method = invokeContractArgs.functionName;
521+
const method = invokeContractArgs.functionName.toString();
522522
const txn = new AssembledTransaction({
523523
...options,
524524
method,

src/contract/spec.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
ScVal,
2929
Uint32,
3030
Uint64,
31+
XdrString,
3132
} from "../xdr/index.js";
3233

3334
export interface Union<T> {
@@ -349,7 +350,7 @@ function isRequired(typeDef: ScSpecTypeDef): boolean {
349350
return typeDef.type !== "scSpecTypeOption";
350351
}
351352

352-
function argsAndRequired(input: { type: ScSpecTypeDef; name: string }[]): {
353+
function argsAndRequired(input: { type: ScSpecTypeDef; name: XdrString }[]): {
353354
properties: object;
354355
required?: string[];
355356
} {
@@ -660,9 +661,7 @@ export class Spec {
660661
return e.value.name.toString() === name;
661662
}
662663
// For UDT entries (struct/union/enum/errorEnum/eventV0), value has .name
663-
return (
664-
(e.value as { name: string | Uint8Array }).name.toString() === name
665-
);
664+
return (e.value as { name: XdrString }).name.toString() === name;
666665
});
667666
if (!entry) {
668667
throw new Error(`no such entry: ${name}`);

src/xdr/generated/account-entry.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { struct } from "../types/struct.js";
22
import { int64 } from "../types/int64.js";
33
import { uint32 } from "../types/uint32.js";
44
import { option } from "../types/option.js";
5-
import { string as string_ } from "../types/string.js";
65
import { array } from "../types/array.js";
76
import { UNBOUNDED_MAX_LENGTH, type XdrType } from "../core/xdr-type.js";
87
import { XdrValue } from "../values/xdr-value.js";
8+
import { XdrString, xdrString } from "../values/xdr-string.js";
99
import { PublicKey, type PublicKeyWire } from "./public-key.js";
1010
import { Thresholds, type ThresholdsWire } from "./thresholds.js";
1111
import { Signer, type SignerWire } from "./signer.js";
@@ -21,7 +21,7 @@ export interface AccountEntryWire {
2121
numSubEntries: number;
2222
inflationDest: PublicKeyWire | null;
2323
flags: number;
24-
homeDomain: string;
24+
homeDomain: XdrString;
2525
thresholds: ThresholdsWire;
2626
signers: SignerWire[];
2727
ext: AccountEntryExtWire;
@@ -66,7 +66,7 @@ export class AccountEntry extends XdrValue {
6666
readonly numSubEntries: number;
6767
readonly inflationDest: PublicKey | null;
6868
readonly flags: number;
69-
readonly homeDomain: string;
69+
readonly homeDomain: XdrString;
7070
readonly thresholds: Thresholds;
7171
readonly signers: Signer[];
7272
readonly ext: AccountEntryExt;
@@ -78,7 +78,7 @@ export class AccountEntry extends XdrValue {
7878
numSubEntries: uint32(),
7979
inflationDest: option(PublicKey.schema),
8080
flags: uint32(),
81-
homeDomain: string_(32),
81+
homeDomain: xdrString(32),
8282
thresholds: Thresholds.schema,
8383
signers: array(Signer.schema, UNBOUNDED_MAX_LENGTH),
8484
ext: AccountEntryExt.schema,
@@ -91,7 +91,7 @@ export class AccountEntry extends XdrValue {
9191
numSubEntries: number;
9292
inflationDest: PublicKey | null;
9393
flags: number;
94-
homeDomain: Uint8Array | string;
94+
homeDomain: XdrString | string | Uint8Array;
9595
thresholds: Thresholds | Uint8Array | string;
9696
signers: Signer[];
9797
ext: AccountEntryExt;
@@ -104,9 +104,9 @@ export class AccountEntry extends XdrValue {
104104
this.inflationDest = input.inflationDest;
105105
this.flags = input.flags;
106106
this.homeDomain =
107-
input.homeDomain instanceof Uint8Array
108-
? new TextDecoder("latin1").decode(input.homeDomain)
109-
: input.homeDomain;
107+
input.homeDomain instanceof XdrString
108+
? input.homeDomain
109+
: new XdrString(input.homeDomain);
110110
this.thresholds =
111111
input.thresholds instanceof Thresholds
112112
? input.thresholds

src/xdr/generated/data-entry.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { struct } from "../types/struct.js";
2-
import { string as string_ } from "../types/string.js";
32
import type { XdrType } from "../core/xdr-type.js";
43
import { XdrValue } from "../values/xdr-value.js";
4+
import { XdrString, xdrString } from "../values/xdr-string.js";
55
import { PublicKey, type PublicKeyWire } from "./public-key.js";
66
import { DataValue, type DataValueWire } from "./data-value.js";
77
import { DataEntryExt, type DataEntryExtWire } from "./data-entry-ext.js";
88

99
export interface DataEntryWire {
1010
accountId: PublicKeyWire;
11-
dataName: string;
11+
dataName: XdrString;
1212
dataValue: DataValueWire;
1313
ext: DataEntryExtWire;
1414
}
@@ -33,29 +33,29 @@ export interface DataEntryWire {
3333
*/
3434
export class DataEntry extends XdrValue {
3535
readonly accountId: PublicKey;
36-
readonly dataName: string;
36+
readonly dataName: XdrString;
3737
readonly dataValue: DataValue;
3838
readonly ext: DataEntryExt;
3939

4040
static readonly schema: XdrType<DataEntryWire> = struct("DataEntry", {
4141
accountId: PublicKey.schema,
42-
dataName: string_(64),
42+
dataName: xdrString(64),
4343
dataValue: DataValue.schema,
4444
ext: DataEntryExt.schema,
4545
});
4646

4747
constructor(input: {
4848
accountId: PublicKey;
49-
dataName: Uint8Array | string;
49+
dataName: XdrString | string | Uint8Array;
5050
dataValue: DataValue | Uint8Array | string;
5151
ext: DataEntryExt;
5252
}) {
5353
super();
5454
this.accountId = input.accountId;
5555
this.dataName =
56-
input.dataName instanceof Uint8Array
57-
? new TextDecoder("latin1").decode(input.dataName)
58-
: input.dataName;
56+
input.dataName instanceof XdrString
57+
? input.dataName
58+
: new XdrString(input.dataName);
5959
this.dataValue =
6060
input.dataValue instanceof DataValue
6161
? input.dataValue

src/xdr/generated/error.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { struct } from "../types/struct.js";
2-
import { string as string_ } from "../types/string.js";
32
import type { XdrType } from "../core/xdr-type.js";
43
import { XdrValue } from "../values/xdr-value.js";
4+
import { XdrString, xdrString } from "../values/xdr-string.js";
55
import { ErrorCode, type ErrorCodeWire } from "./error-code.js";
66

77
export interface ErrorWire {
88
code: ErrorCodeWire;
9-
msg: string;
9+
msg: XdrString;
1010
}
1111

1212
/**
@@ -20,20 +20,21 @@ export interface ErrorWire {
2020
*/
2121
export class Error extends XdrValue {
2222
readonly code: ErrorCode;
23-
readonly msg: string;
23+
readonly msg: XdrString;
2424

2525
static readonly schema: XdrType<ErrorWire> = struct("Error", {
2626
code: ErrorCode.schema,
27-
msg: string_(100),
27+
msg: xdrString(100),
2828
});
2929

30-
constructor(input: { code: ErrorCode; msg: Uint8Array | string }) {
30+
constructor(input: {
31+
code: ErrorCode;
32+
msg: XdrString | string | Uint8Array;
33+
}) {
3134
super();
3235
this.code = input.code;
3336
this.msg =
34-
input.msg instanceof Uint8Array
35-
? new TextDecoder("latin1").decode(input.msg)
36-
: input.msg;
37+
input.msg instanceof XdrString ? input.msg : new XdrString(input.msg);
3738
}
3839

3940
toXdrObject(): ErrorWire {

src/xdr/generated/hello.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { struct } from "../types/struct.js";
22
import { uint32 } from "../types/uint32.js";
3-
import { string as string_ } from "../types/string.js";
43
import { int32 } from "../types/int32.js";
54
import { opaque } from "../types/opaque.js";
65
import type { XdrType } from "../core/xdr-type.js";
76
import { XdrValue } from "../values/xdr-value.js";
7+
import { XdrString, xdrString } from "../values/xdr-string.js";
88
import { Hash, type HashWire } from "./hash.js";
99
import { PublicKey, type PublicKeyWire } from "./public-key.js";
1010
import { AuthCert, type AuthCertWire } from "./auth-cert.js";
@@ -14,7 +14,7 @@ export interface HelloWire {
1414
overlayVersion: number;
1515
overlayMinVersion: number;
1616
networkId: HashWire;
17-
versionStr: string;
17+
versionStr: XdrString;
1818
listeningPort: number;
1919
peerId: PublicKeyWire;
2020
cert: AuthCertWire;
@@ -42,7 +42,7 @@ export class Hello extends XdrValue {
4242
readonly overlayVersion: number;
4343
readonly overlayMinVersion: number;
4444
readonly networkId: Hash;
45-
readonly versionStr: string;
45+
readonly versionStr: XdrString;
4646
readonly listeningPort: number;
4747
readonly peerId: PublicKey;
4848
readonly cert: AuthCert;
@@ -53,7 +53,7 @@ export class Hello extends XdrValue {
5353
overlayVersion: uint32(),
5454
overlayMinVersion: uint32(),
5555
networkId: Hash.schema,
56-
versionStr: string_(100),
56+
versionStr: xdrString(100),
5757
listeningPort: int32(),
5858
peerId: PublicKey.schema,
5959
cert: AuthCert.schema,
@@ -65,7 +65,7 @@ export class Hello extends XdrValue {
6565
overlayVersion: number;
6666
overlayMinVersion: number;
6767
networkId: Hash | Uint8Array | string;
68-
versionStr: Uint8Array | string;
68+
versionStr: XdrString | string | Uint8Array;
6969
listeningPort: number;
7070
peerId: PublicKey;
7171
cert: AuthCert;
@@ -80,9 +80,9 @@ export class Hello extends XdrValue {
8080
? input.networkId
8181
: new Hash(input.networkId);
8282
this.versionStr =
83-
input.versionStr instanceof Uint8Array
84-
? new TextDecoder("latin1").decode(input.versionStr)
85-
: input.versionStr;
83+
input.versionStr instanceof XdrString
84+
? input.versionStr
85+
: new XdrString(input.versionStr);
8686
this.listeningPort = input.listeningPort;
8787
this.peerId = input.peerId;
8888
this.cert = input.cert;

0 commit comments

Comments
 (0)