@sorandomains/owner 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/funding-owner.d.ts +42 -1
- package/dist/funding-owner.js +88 -1
- package/dist/funding-reader.d.ts +29 -1
- package/dist/funding-reader.js +119 -4
- package/dist/funding-types.d.ts +88 -0
- package/dist/funding-types.js +161 -0
- package/dist/native-codec.js +2 -2
- package/dist/native-transport.d.ts +2 -0
- package/dist/native-transport.js +10 -0
- package/dist/native-upgrades.d.ts +4 -4
- package/dist/native-upgrades.js +9 -2
- package/dist/payment.d.ts +3 -1
- package/dist/payment.js +20 -14
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Lookup is the read entry point in `@sorandomains/lookup` 0.10.0 or later.
|
|
|
12
12
|
|
|
13
13
|
Version 0.12.1 rejects simulated transactions whose source, operation, contract, method, arguments or authorization differ from the requested legacy account-signed write. Unexpected authorizers and nested calls are rejected before signing.
|
|
14
14
|
|
|
15
|
-
Releases older than 0.11.0 (and any built against Lookup 0.6.0 to 0.9.0) default to the retired 5 and 6 September 2026 testnet stacks; upgrade rather than pinning a retired Registry. See [Older versions and retired stacks](
|
|
15
|
+
Releases older than 0.11.0 (and any built against Lookup 0.6.0 to 0.9.0) default to the retired 5 and 6 September 2026 testnet stacks; upgrade rather than pinning a retired Registry. See [Older versions and retired stacks](../README.md#older-versions-and-retired-stacks).
|
|
16
16
|
|
|
17
17
|
Ownership, issuer, holder and treasury inputs remain G/C account or contract
|
|
18
18
|
addresses. Muxed M addresses are payment destinations only: issue the name to its
|
|
@@ -119,7 +119,7 @@ Read the [native claim APIs, security boundaries and complete signup flow](https
|
|
|
119
119
|
|
|
120
120
|
## Verified testnet deployment
|
|
121
121
|
|
|
122
|
-
See the [deployment manifest](../../
|
|
122
|
+
See the [deployment manifest](../../deploy/testnet/deployment.json) for confirmed code hashes, transaction receipts and verification scope. Network passphrase: `Test SDF Network ; September 2015`.
|
|
123
123
|
|
|
124
124
|
| Contract | Address |
|
|
125
125
|
|---|---|
|
package/dist/funding-owner.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FundingReader, type FundingOptions } from "./funding-reader.js";
|
|
2
|
-
import { type SponsorPolicy } from "./funding-types.js";
|
|
2
|
+
import { type SponsorPolicy, type StoragePlan, type UpkeepPolicy, type ExecutionPolicy } from "./funding-types.js";
|
|
3
3
|
import { type NativeSigner, type NativeWriteOptions } from "./native-transport.js";
|
|
4
4
|
/** Direct owner operations work even when the sponsorship service is offline. */
|
|
5
5
|
export declare class SoranFunding extends FundingReader {
|
|
@@ -42,4 +42,45 @@ export declare class SoranFunding extends FundingReader {
|
|
|
42
42
|
ledger: number;
|
|
43
43
|
value: unknown;
|
|
44
44
|
}>;
|
|
45
|
+
activateStorage(namespace: string, plan: StoragePlan, retention: number, options?: NativeWriteOptions): Promise<{
|
|
46
|
+
hash: string;
|
|
47
|
+
ledger: number;
|
|
48
|
+
value: unknown;
|
|
49
|
+
}>;
|
|
50
|
+
private upkeepController;
|
|
51
|
+
configureUpkeep(id: string, policy: UpkeepPolicy, deposit: bigint, options?: NativeWriteOptions): Promise<{
|
|
52
|
+
hash: string;
|
|
53
|
+
ledger: number;
|
|
54
|
+
value: unknown;
|
|
55
|
+
}>;
|
|
56
|
+
depositUpkeep(id: string, amount: bigint, options?: NativeWriteOptions): Promise<{
|
|
57
|
+
hash: string;
|
|
58
|
+
ledger: number;
|
|
59
|
+
value: unknown;
|
|
60
|
+
}>;
|
|
61
|
+
pauseUpkeep(id: string, paused: boolean, options?: NativeWriteOptions): Promise<{
|
|
62
|
+
hash: string;
|
|
63
|
+
ledger: number;
|
|
64
|
+
value: unknown;
|
|
65
|
+
}>;
|
|
66
|
+
withdrawUpkeep(id: string, amount: bigint, recipient: string, options?: NativeWriteOptions): Promise<{
|
|
67
|
+
hash: string;
|
|
68
|
+
ledger: number;
|
|
69
|
+
value: unknown;
|
|
70
|
+
}>;
|
|
71
|
+
configureExecution(id: string, policy: ExecutionPolicy, deposit: bigint, options?: NativeWriteOptions): Promise<{
|
|
72
|
+
hash: string;
|
|
73
|
+
ledger: number;
|
|
74
|
+
value: unknown;
|
|
75
|
+
}>;
|
|
76
|
+
depositExecution(id: string, amount: bigint, options?: NativeWriteOptions): Promise<{
|
|
77
|
+
hash: string;
|
|
78
|
+
ledger: number;
|
|
79
|
+
value: unknown;
|
|
80
|
+
}>;
|
|
81
|
+
withdrawExecution(id: string, amount: bigint, recipient: string, maxPayoutFee: bigint, options?: NativeWriteOptions): Promise<{
|
|
82
|
+
hash: string;
|
|
83
|
+
ledger: number;
|
|
84
|
+
value: unknown;
|
|
85
|
+
}>;
|
|
45
86
|
}
|
package/dist/funding-owner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Asset } from "@stellar/stellar-sdk";
|
|
2
2
|
import { FundingReader } from "./funding-reader.js";
|
|
3
|
-
import { FundingError, sponsorPolicyToScVal, } from "./funding-types.js";
|
|
3
|
+
import { FundingError, sponsorPolicyToScVal, storagePlanToScVal, upkeepPolicyToScVal, executionPolicyToScVal, } from "./funding-types.js";
|
|
4
4
|
import { address, namespaceNode, sc, unhex } from "./native-codec.js";
|
|
5
5
|
import { sendNative, } from "./native-transport.js";
|
|
6
6
|
/** Direct owner operations work even when the sponsorship service is offline. */
|
|
@@ -94,4 +94,91 @@ export class SoranFunding extends FundingReader {
|
|
|
94
94
|
async close(id, options = {}) {
|
|
95
95
|
return this.write("close_fund", [sc.bytes(unhex(id))], await this.controller(id), options);
|
|
96
96
|
}
|
|
97
|
+
async activateStorage(namespace, plan, retention, options = {}) {
|
|
98
|
+
await this.upkeepConfiguration();
|
|
99
|
+
if (!Number.isSafeInteger(retention) || retention < 1)
|
|
100
|
+
throw new FundingError("Invalid retention");
|
|
101
|
+
const node = sc.bytes(namespaceNode(namespace));
|
|
102
|
+
const owner = address((await this.read(this.registryId, "owner_of", [node])).value, "identity", "namespace owner");
|
|
103
|
+
return this.write("upkeep_activate", [node, storagePlanToScVal(plan), sc.u32(retention)], owner, options);
|
|
104
|
+
}
|
|
105
|
+
async upkeepController(id) {
|
|
106
|
+
await this.upkeepConfiguration();
|
|
107
|
+
const p = await this.upkeepPosition(id);
|
|
108
|
+
if (!p)
|
|
109
|
+
throw new FundingError("Prepay namespace storage first");
|
|
110
|
+
return p.controller;
|
|
111
|
+
}
|
|
112
|
+
async configureUpkeep(id, policy, deposit, options = {}) {
|
|
113
|
+
if (deposit < 0n)
|
|
114
|
+
throw new FundingError("Invalid deposit");
|
|
115
|
+
const owner = await this.upkeepController(id);
|
|
116
|
+
return this.write("upkeep_configure", [sc.bytes(unhex(id)), upkeepPolicyToScVal(policy), sc.i128(deposit)], owner, options, deposit > 0n
|
|
117
|
+
? [
|
|
118
|
+
{
|
|
119
|
+
contract: Asset.native().contractId(this.passphrase),
|
|
120
|
+
method: "transfer",
|
|
121
|
+
args: [
|
|
122
|
+
sc.address(owner),
|
|
123
|
+
sc.address(this.fundingId),
|
|
124
|
+
sc.i128(deposit),
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
]
|
|
128
|
+
: []);
|
|
129
|
+
}
|
|
130
|
+
async depositUpkeep(id, amount, options = {}) {
|
|
131
|
+
if (amount <= 0n)
|
|
132
|
+
throw new FundingError("Deposit must be positive");
|
|
133
|
+
const owner = await this.upkeepController(id);
|
|
134
|
+
return this.write("upkeep_deposit", [sc.bytes(unhex(id)), sc.i128(amount)], owner, options, [
|
|
135
|
+
{
|
|
136
|
+
contract: Asset.native().contractId(this.passphrase),
|
|
137
|
+
method: "transfer",
|
|
138
|
+
args: [
|
|
139
|
+
sc.address(owner),
|
|
140
|
+
sc.address(this.fundingId),
|
|
141
|
+
sc.i128(amount),
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
}
|
|
146
|
+
async pauseUpkeep(id, paused, options = {}) {
|
|
147
|
+
return this.write("upkeep_pause", [sc.bytes(unhex(id)), sc.bool(paused)], await this.upkeepController(id), options);
|
|
148
|
+
}
|
|
149
|
+
async withdrawUpkeep(id, amount, recipient, options = {}) {
|
|
150
|
+
if (amount <= 0n || recipient === this.fundingId)
|
|
151
|
+
throw new FundingError("Invalid withdrawal");
|
|
152
|
+
return this.write("upkeep_withdraw", [
|
|
153
|
+
sc.bytes(unhex(id)),
|
|
154
|
+
sc.address(address(recipient, "identity", "recipient")),
|
|
155
|
+
sc.i128(amount),
|
|
156
|
+
], await this.upkeepController(id), options);
|
|
157
|
+
}
|
|
158
|
+
async configureExecution(id, policy, deposit, options = {}) {
|
|
159
|
+
if (deposit < 0n)
|
|
160
|
+
throw new FundingError("Invalid execution deposit");
|
|
161
|
+
const c = await this.executionConfiguration();
|
|
162
|
+
if (policy.wallet !== c.wallet)
|
|
163
|
+
throw new FundingError("Execution wallet differs from the approved deployment", "authorization");
|
|
164
|
+
const owner = await this.upkeepController(id);
|
|
165
|
+
return this.write("execution_configure", [sc.bytes(unhex(id)), executionPolicyToScVal(policy), sc.i128(deposit)], owner, options, deposit > 0n ? [{ contract: Asset.native().contractId(this.passphrase), method: "transfer", args: [sc.address(owner), sc.address(c.wallet), sc.i128(deposit)] }] : []);
|
|
166
|
+
}
|
|
167
|
+
async depositExecution(id, amount, options = {}) {
|
|
168
|
+
if (amount <= 0n)
|
|
169
|
+
throw new FundingError("Execution deposit must be positive");
|
|
170
|
+
const owner = await this.upkeepController(id), a = await this.executionApproval(id), c = await this.executionConfiguration();
|
|
171
|
+
if (!a || a.policy.wallet !== c.wallet)
|
|
172
|
+
throw new FundingError("Review prepaid execution terms first", "authorization");
|
|
173
|
+
return this.write("execution_deposit", [sc.bytes(unhex(id)), sc.i128(amount)], owner, options, [{ contract: Asset.native().contractId(this.passphrase), method: "transfer", args: [sc.address(owner), sc.address(c.wallet), sc.i128(amount)] }]);
|
|
174
|
+
}
|
|
175
|
+
async withdrawExecution(id, amount, recipient, maxPayoutFee, options = {}) {
|
|
176
|
+
if (amount <= 0n || maxPayoutFee < 100n || maxPayoutFee > 4294967295n)
|
|
177
|
+
throw new FundingError("Invalid prepaid withdrawal or payout fee ceiling");
|
|
178
|
+
address(recipient, "account", "withdrawal recipient");
|
|
179
|
+
const a = await this.executionApproval(id);
|
|
180
|
+
if (!a || recipient === a.policy.wallet || amount + maxPayoutFee > a.depositedTotal)
|
|
181
|
+
throw new FundingError("Invalid execution withdrawal");
|
|
182
|
+
return this.write("execution_withdraw_request", [sc.bytes(unhex(id)), sc.address(recipient), sc.i128(amount), sc.i128(maxPayoutFee)], await this.upkeepController(id), options);
|
|
183
|
+
}
|
|
97
184
|
}
|
package/dist/funding-reader.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { rpc, xdr } from "@stellar/stellar-sdk";
|
|
2
|
-
import { type SponsorQuote } from "./funding-types.js";
|
|
2
|
+
import { type SponsorQuote, type StoragePlan, type UpkeepQuote, type ExecutionQuote } from "./funding-types.js";
|
|
3
3
|
export type FundingOptions = {
|
|
4
4
|
fundingId: string;
|
|
5
5
|
registryId: string;
|
|
@@ -40,4 +40,32 @@ export declare class FundingReader {
|
|
|
40
40
|
policy(id: string): Promise<import("./funding-types.js").SponsorPolicy | null>;
|
|
41
41
|
actorState(id: string, actor: string): Promise<import("./funding-types.js").SponsorActorState>;
|
|
42
42
|
validateQuote(quote: SponsorQuote): Promise<void>;
|
|
43
|
+
upkeepConfiguration(): Promise<{
|
|
44
|
+
registry: string;
|
|
45
|
+
nativeAsset: string;
|
|
46
|
+
lookup: string;
|
|
47
|
+
primary: string;
|
|
48
|
+
governance: string;
|
|
49
|
+
paused: boolean;
|
|
50
|
+
revision: bigint;
|
|
51
|
+
ledger: number;
|
|
52
|
+
}>;
|
|
53
|
+
isUpkeepEnrolled(namespace: string): Promise<boolean>;
|
|
54
|
+
currentUpkeep(namespace: string): Promise<{
|
|
55
|
+
id: string;
|
|
56
|
+
position: import("./funding-types.js").UpkeepPosition | null;
|
|
57
|
+
}>;
|
|
58
|
+
upkeepPosition(id: string): Promise<import("./funding-types.js").UpkeepPosition | null>;
|
|
59
|
+
upkeepPolicy(id: string): Promise<import("./funding-types.js").UpkeepPolicy | null>;
|
|
60
|
+
storagePlan(namespace: string): Promise<StoragePlan>;
|
|
61
|
+
upkeepCoverage(id: string, plan: StoragePlan): Promise<number[]>;
|
|
62
|
+
validateUpkeepQuote(q: UpkeepQuote): Promise<void>;
|
|
63
|
+
executionConfiguration(): Promise<{
|
|
64
|
+
version: 2;
|
|
65
|
+
wallet: string;
|
|
66
|
+
}>;
|
|
67
|
+
executionApproval(id: string): Promise<import("./funding-types.js").ExecutionApproval | null>;
|
|
68
|
+
executionWithdrawal(id: string, nonce: bigint): Promise<import("./funding-types.js").ExecutionWithdrawal | null>;
|
|
69
|
+
executionWithdrawalNonce(id: string): Promise<bigint>;
|
|
70
|
+
validateExecutionQuote(q: ExecutionQuote): Promise<void>;
|
|
43
71
|
}
|
package/dist/funding-reader.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Account, Asset, Contract, Networks, StrKey, TransactionBuilder, hash, rpc, scValToNative, } from "@stellar/stellar-sdk";
|
|
2
2
|
import { address, hex, namespaceNode, sc, unhex, utf8, } from "./native-codec.js";
|
|
3
|
-
import { FundingError, fundingConfigurationFromNative, fundingPositionFromNative, fundingPositionId, sponsorActorFromNative, sponsorPolicyFromNative, sponsorQuoteToScVal, } from "./funding-types.js";
|
|
3
|
+
import { FundingError, fundingConfigurationFromNative, fundingPositionFromNative, fundingPositionId, sponsorActorFromNative, sponsorPolicyFromNative, sponsorQuoteToScVal, upkeepPositionId, upkeepPositionFromNative, upkeepPolicyFromNative, storagePlanToScVal, upkeepQuoteToScVal, executionApprovalFromNative, executionWithdrawalFromNative, executionQuoteToScVal, } from "./funding-types.js";
|
|
4
4
|
export class FundingReader {
|
|
5
5
|
fundingId;
|
|
6
6
|
registryId;
|
|
@@ -18,9 +18,16 @@ export class FundingReader {
|
|
|
18
18
|
if (this.passphrase !== Networks.TESTNET)
|
|
19
19
|
throw new FundingError("Funding V1 is currently restricted to testnet");
|
|
20
20
|
this.networkId = hex(hash(utf8(this.passphrase)));
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if (options.server) {
|
|
22
|
+
// A supplied client retains its caller-reviewed transport configuration.
|
|
23
|
+
this.server = options.server;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.server = new rpc.Server(options.rpcUrl ?? "https://soroban-testnet.stellar.org");
|
|
27
|
+
// SDK 17 ignores the constructor's timeout option. Bound the HTTP client
|
|
28
|
+
// actually used by simulation and ledger requests, as the API RPC factory does.
|
|
29
|
+
this.server.httpClient.defaults.timeout = 15_000;
|
|
30
|
+
}
|
|
24
31
|
}
|
|
25
32
|
async read(contract, method, args, minimumLedger = 1) {
|
|
26
33
|
const source = new Account(StrKey.encodeEd25519PublicKey(new Uint8Array(32)), "0");
|
|
@@ -42,6 +49,11 @@ export class FundingReader {
|
|
|
42
49
|
result.latestLedger < minimumLedger)
|
|
43
50
|
throw new FundingError("Cannot verify funding contract state: " +
|
|
44
51
|
("error" in result ? result.error : "missing or stale response"), "unavailable");
|
|
52
|
+
const data = result.transactionData.build();
|
|
53
|
+
if (data.ext.type !== "v0" &&
|
|
54
|
+
(data.ext.type !== "resourceExt" ||
|
|
55
|
+
data.ext.value.archivedSorobanEntries.length > 0))
|
|
56
|
+
throw new FundingError("Cannot verify live funding state: archived entries require explicit restoration", "unavailable");
|
|
45
57
|
return {
|
|
46
58
|
value: scValToNative(result.result.retval),
|
|
47
59
|
ledger: result.latestLedger,
|
|
@@ -99,4 +111,107 @@ export class FundingReader {
|
|
|
99
111
|
await this.configuration();
|
|
100
112
|
await this.read(this.fundingId, "quote_status", [sponsorQuoteToScVal(quote)], quote.issuedLedger);
|
|
101
113
|
}
|
|
114
|
+
async upkeepConfiguration() {
|
|
115
|
+
const config = await this.configuration();
|
|
116
|
+
if ((await this.read(this.fundingId, "upkeep_version", [], config.ledger))
|
|
117
|
+
.value !== 1)
|
|
118
|
+
throw new FundingError("Owner-funded storage is not installed on this deployment", "unavailable");
|
|
119
|
+
return config;
|
|
120
|
+
}
|
|
121
|
+
async isUpkeepEnrolled(namespace) {
|
|
122
|
+
await this.upkeepConfiguration();
|
|
123
|
+
const r = await this.read(this.fundingId, "upkeep_enrolled", [sc.bytes(namespaceNode(namespace))]);
|
|
124
|
+
if (typeof r.value !== "boolean")
|
|
125
|
+
throw new FundingError("Namespace storage billing boundary is unavailable", "unavailable");
|
|
126
|
+
return r.value;
|
|
127
|
+
}
|
|
128
|
+
async currentUpkeep(namespace) {
|
|
129
|
+
await this.upkeepConfiguration();
|
|
130
|
+
const node = hex(namespaceNode(namespace));
|
|
131
|
+
const epoch = await this.read(this.registryId, "owner_epoch", [
|
|
132
|
+
sc.bytes(unhex(node)),
|
|
133
|
+
]);
|
|
134
|
+
if (typeof epoch.value !== "bigint" || epoch.value < 1n)
|
|
135
|
+
throw new FundingError("Namespace ownership epoch unavailable", "unavailable");
|
|
136
|
+
const id = upkeepPositionId(node, epoch.value);
|
|
137
|
+
return { id, position: await this.upkeepPosition(id) };
|
|
138
|
+
}
|
|
139
|
+
async upkeepPosition(id) {
|
|
140
|
+
const r = await this.read(this.fundingId, "upkeep_position", [
|
|
141
|
+
sc.bytes(unhex(id)),
|
|
142
|
+
]);
|
|
143
|
+
if (r.value === null || r.value === undefined)
|
|
144
|
+
return null;
|
|
145
|
+
const p = upkeepPositionFromNative(r.value);
|
|
146
|
+
if (upkeepPositionId(p.namespace, p.ownerEpoch) !== id)
|
|
147
|
+
throw new FundingError("Upkeep ownership epoch does not match", "unavailable");
|
|
148
|
+
return p;
|
|
149
|
+
}
|
|
150
|
+
async upkeepPolicy(id) {
|
|
151
|
+
const r = await this.read(this.fundingId, "upkeep_policy", [
|
|
152
|
+
sc.bytes(unhex(id)),
|
|
153
|
+
]);
|
|
154
|
+
return r.value === null || r.value === undefined
|
|
155
|
+
? null
|
|
156
|
+
: upkeepPolicyFromNative(r.value);
|
|
157
|
+
}
|
|
158
|
+
async storagePlan(namespace) {
|
|
159
|
+
await this.upkeepConfiguration();
|
|
160
|
+
const pair = (await this.read(this.registryId, "native_contracts", [
|
|
161
|
+
sc.bytes(namespaceNode(namespace)),
|
|
162
|
+
])).value;
|
|
163
|
+
if (!Array.isArray(pair) || pair.length !== 2)
|
|
164
|
+
throw new FundingError("Native namespace pair unavailable", "unavailable");
|
|
165
|
+
const plan = {
|
|
166
|
+
registrar: address(pair[0], "contract", "Registrar"),
|
|
167
|
+
resolver: address(pair[1], "contract", "Resolver"),
|
|
168
|
+
registrarKeys: [],
|
|
169
|
+
resolverKeys: [],
|
|
170
|
+
};
|
|
171
|
+
for (const c of [plan.registrar, plan.resolver])
|
|
172
|
+
if ((await this.read(c, "storage_upkeep_version", [])).value !== 1)
|
|
173
|
+
throw new FundingError("Namespace storage upgrade required", "unavailable");
|
|
174
|
+
return plan;
|
|
175
|
+
}
|
|
176
|
+
async upkeepCoverage(id, plan) {
|
|
177
|
+
const r = await this.read(this.fundingId, "upkeep_coverage", [
|
|
178
|
+
sc.bytes(unhex(id)),
|
|
179
|
+
storagePlanToScVal(plan),
|
|
180
|
+
]);
|
|
181
|
+
if (!Array.isArray(r.value) ||
|
|
182
|
+
r.value.some((v) => !Number.isSafeInteger(v) || v < 0))
|
|
183
|
+
throw new FundingError("Invalid storage coverage response", "unavailable");
|
|
184
|
+
return r.value;
|
|
185
|
+
}
|
|
186
|
+
async validateUpkeepQuote(q) {
|
|
187
|
+
if (q.funding !== this.fundingId || q.network !== this.networkId)
|
|
188
|
+
throw new FundingError("Upkeep quote belongs to another deployment", "authorization");
|
|
189
|
+
await this.upkeepConfiguration();
|
|
190
|
+
await this.read(this.fundingId, "upkeep_quote_status", [upkeepQuoteToScVal(q)], q.issuedLedger);
|
|
191
|
+
}
|
|
192
|
+
async executionConfiguration() {
|
|
193
|
+
await this.upkeepConfiguration();
|
|
194
|
+
if ((await this.read(this.fundingId, "execution_version", [])).value !== 2)
|
|
195
|
+
throw new FundingError("Prepaid execution contract upgrade required", "unavailable");
|
|
196
|
+
return { version: 2, wallet: address((await this.read(this.fundingId, "execution_wallet", [])).value, "account", "custodial execution wallet") };
|
|
197
|
+
}
|
|
198
|
+
async executionApproval(id) {
|
|
199
|
+
const value = (await this.read(this.fundingId, "execution_approval", [sc.bytes(unhex(id))])).value;
|
|
200
|
+
return value == null ? null : executionApprovalFromNative(value);
|
|
201
|
+
}
|
|
202
|
+
async executionWithdrawal(id, nonce) {
|
|
203
|
+
const value = (await this.read(this.fundingId, "execution_withdrawal", [sc.bytes(unhex(id)), sc.u64(nonce)])).value;
|
|
204
|
+
return value == null ? null : executionWithdrawalFromNative(value);
|
|
205
|
+
}
|
|
206
|
+
async executionWithdrawalNonce(id) {
|
|
207
|
+
const value = (await this.read(this.fundingId, "execution_withdrawal_nonce", [sc.bytes(unhex(id))])).value;
|
|
208
|
+
if (typeof value !== "bigint" || value < 0n)
|
|
209
|
+
throw new FundingError("Invalid withdrawal cursor", "unavailable");
|
|
210
|
+
return value;
|
|
211
|
+
}
|
|
212
|
+
async validateExecutionQuote(q) {
|
|
213
|
+
if (q.funding !== this.fundingId || q.network !== this.networkId)
|
|
214
|
+
throw new FundingError("Execution quote belongs to another deployment", "authorization");
|
|
215
|
+
await this.read(this.fundingId, "execution_quote_status", [executionQuoteToScVal(q)], q.issuedLedger);
|
|
216
|
+
}
|
|
102
217
|
}
|
package/dist/funding-types.d.ts
CHANGED
|
@@ -102,3 +102,91 @@ export declare function fundingPositionId(namespace: string, ownerEpoch: bigint)
|
|
|
102
102
|
export declare const encodeSponsorQuote: (v: SponsorQuote) => string;
|
|
103
103
|
export declare function decodeSponsorQuote(encoded: string): SponsorQuote;
|
|
104
104
|
export declare const FUNDING_ERRORS: Record<number, string>;
|
|
105
|
+
/** Owner-funded namespace retention. Kept separate from registration coverage. */
|
|
106
|
+
export type StoragePlan = {
|
|
107
|
+
registrar: string;
|
|
108
|
+
resolver: string;
|
|
109
|
+
registrarKeys: xdr.ScVal[];
|
|
110
|
+
resolverKeys: xdr.ScVal[];
|
|
111
|
+
};
|
|
112
|
+
export type UpkeepPolicy = {
|
|
113
|
+
relayer: string;
|
|
114
|
+
renewalCap: bigint;
|
|
115
|
+
dailyCap: bigint;
|
|
116
|
+
totalCap: bigint;
|
|
117
|
+
retention: number;
|
|
118
|
+
renewBefore: number;
|
|
119
|
+
quoteLifetime: number;
|
|
120
|
+
validUntil: bigint;
|
|
121
|
+
};
|
|
122
|
+
export type UpkeepReceipt = {
|
|
123
|
+
quoteHash: string;
|
|
124
|
+
quoteId: string;
|
|
125
|
+
nonce: bigint;
|
|
126
|
+
charge: bigint;
|
|
127
|
+
balance: bigint;
|
|
128
|
+
ledger: number;
|
|
129
|
+
coveredUntil: number;
|
|
130
|
+
};
|
|
131
|
+
export type UpkeepPosition = Omit<FundingPosition, "status"> & {
|
|
132
|
+
status: "paused" | "active" | "closed";
|
|
133
|
+
nonce: bigint;
|
|
134
|
+
lastReceipt: UpkeepReceipt | null;
|
|
135
|
+
};
|
|
136
|
+
export type UpkeepQuote = {
|
|
137
|
+
network: string;
|
|
138
|
+
funding: string;
|
|
139
|
+
configRevision: bigint;
|
|
140
|
+
fundId: string;
|
|
141
|
+
policyRevision: bigint;
|
|
142
|
+
nonce: bigint;
|
|
143
|
+
quoteId: string;
|
|
144
|
+
plan: StoragePlan;
|
|
145
|
+
charge: bigint;
|
|
146
|
+
issuedLedger: number;
|
|
147
|
+
expiresLedger: number;
|
|
148
|
+
};
|
|
149
|
+
export type ExecutionPolicy = {
|
|
150
|
+
wallet: string;
|
|
151
|
+
limits: UpkeepPolicy;
|
|
152
|
+
serviceFeeCap: bigint;
|
|
153
|
+
termsVersion: 2;
|
|
154
|
+
serviceFeeBps: 500;
|
|
155
|
+
payFailedFees: true;
|
|
156
|
+
};
|
|
157
|
+
export type ExecutionApproval = {
|
|
158
|
+
policy: ExecutionPolicy;
|
|
159
|
+
depositedTotal: bigint;
|
|
160
|
+
depositNonce: bigint;
|
|
161
|
+
};
|
|
162
|
+
export type ExecutionQuote = Omit<UpkeepQuote, "charge"> & {
|
|
163
|
+
maxNetworkFee: bigint;
|
|
164
|
+
};
|
|
165
|
+
export type ExecutionReceipt = {
|
|
166
|
+
quoteHash: string;
|
|
167
|
+
quoteId: string;
|
|
168
|
+
nonce: bigint;
|
|
169
|
+
ledger: number;
|
|
170
|
+
coveredUntil: number;
|
|
171
|
+
};
|
|
172
|
+
export type ExecutionWithdrawal = {
|
|
173
|
+
controller: string;
|
|
174
|
+
recipient: string;
|
|
175
|
+
amount: bigint;
|
|
176
|
+
maxNetworkFee: bigint;
|
|
177
|
+
nonce: bigint;
|
|
178
|
+
};
|
|
179
|
+
export declare function executionPolicyToScVal(p: ExecutionPolicy): xdr.ScVal;
|
|
180
|
+
export declare function executionApprovalFromNative(value: unknown): ExecutionApproval;
|
|
181
|
+
export declare const executionReceiptFromNative: (value: unknown) => ExecutionReceipt;
|
|
182
|
+
export declare const executionReceiptToScVal: (value: ExecutionReceipt) => xdr.ScVal;
|
|
183
|
+
export declare const executionWithdrawalFromNative: (value: unknown) => ExecutionWithdrawal;
|
|
184
|
+
export declare const upkeepPositionFromNative: (v: unknown) => UpkeepPosition;
|
|
185
|
+
export declare const upkeepPolicyFromNative: (v: unknown) => UpkeepPolicy;
|
|
186
|
+
export declare const upkeepReceiptToScVal: (v: UpkeepReceipt) => xdr.ScVal;
|
|
187
|
+
export declare const upkeepReceiptFromNative: (v: unknown) => UpkeepReceipt;
|
|
188
|
+
export declare function upkeepPositionId(namespace: string, epoch: bigint): string;
|
|
189
|
+
export declare function upkeepPolicyToScVal(p: UpkeepPolicy): xdr.ScVal;
|
|
190
|
+
export declare function storagePlanToScVal(p: StoragePlan): xdr.ScVal;
|
|
191
|
+
export declare function upkeepQuoteToScVal(q: UpkeepQuote): xdr.ScVal;
|
|
192
|
+
export declare function executionQuoteToScVal(q: ExecutionQuote): xdr.ScVal;
|
package/dist/funding-types.js
CHANGED
|
@@ -277,3 +277,164 @@ export const FUNDING_ERRORS = {
|
|
|
277
277
|
30: "Name state changed",
|
|
278
278
|
31: "Upgrade does not match",
|
|
279
279
|
};
|
|
280
|
+
const UPKEEP_POLICY = model({
|
|
281
|
+
relayer: G,
|
|
282
|
+
renewalCap: MONEY,
|
|
283
|
+
dailyCap: MONEY,
|
|
284
|
+
totalCap: MONEY,
|
|
285
|
+
retention: U32,
|
|
286
|
+
renewBefore: U32,
|
|
287
|
+
quoteLifetime: U32,
|
|
288
|
+
validUntil: U64,
|
|
289
|
+
});
|
|
290
|
+
const EXECUTION_POLICY = model({ wallet: G, limits: UPKEEP_POLICY, serviceFeeCap: MONEY, termsVersion: U32, serviceFeeBps: U32, payFailedFees: BOOL });
|
|
291
|
+
const EXECUTION_APPROVAL = model({ policy: EXECUTION_POLICY, depositedTotal: MONEY, depositNonce: U64 });
|
|
292
|
+
const EXECUTION_RECEIPT = model({ quoteHash: HEX, quoteId: HEX, nonce: U64, ledger: U32, coveredUntil: U32 });
|
|
293
|
+
const EXECUTION_WITHDRAWAL = model({ controller: ID, recipient: G, amount: MONEY, maxNetworkFee: MONEY, nonce: U64 });
|
|
294
|
+
function validateExecutionPolicy(p) {
|
|
295
|
+
upkeepPolicyToScVal(p.limits);
|
|
296
|
+
if (p.termsVersion !== 2 || p.serviceFeeBps !== 500 || p.payFailedFees !== true || p.wallet !== p.limits.relayer ||
|
|
297
|
+
p.serviceFeeCap < 0n || p.serviceFeeCap > p.limits.renewalCap)
|
|
298
|
+
throw new FundingError("Explicit prepaid custody, failed fees and capped 5% approval are required");
|
|
299
|
+
}
|
|
300
|
+
export function executionPolicyToScVal(p) {
|
|
301
|
+
const encoded = EXECUTION_POLICY.encode(p);
|
|
302
|
+
validateExecutionPolicy(p);
|
|
303
|
+
return encoded;
|
|
304
|
+
}
|
|
305
|
+
export function executionApprovalFromNative(value) {
|
|
306
|
+
const a = EXECUTION_APPROVAL.decode(value);
|
|
307
|
+
validateExecutionPolicy(a.policy);
|
|
308
|
+
return a;
|
|
309
|
+
}
|
|
310
|
+
export const executionReceiptFromNative = (value) => EXECUTION_RECEIPT.decode(value);
|
|
311
|
+
export const executionReceiptToScVal = (value) => EXECUTION_RECEIPT.encode(value);
|
|
312
|
+
export const executionWithdrawalFromNative = (value) => EXECUTION_WITHDRAWAL.decode(value);
|
|
313
|
+
const UPKEEP_RECEIPT = model({
|
|
314
|
+
quoteHash: HEX,
|
|
315
|
+
quoteId: HEX,
|
|
316
|
+
nonce: U64,
|
|
317
|
+
charge: MONEY,
|
|
318
|
+
balance: MONEY,
|
|
319
|
+
ledger: U32,
|
|
320
|
+
coveredUntil: U32,
|
|
321
|
+
});
|
|
322
|
+
const UPKEEP_LAST = {
|
|
323
|
+
encode(v) {
|
|
324
|
+
return xdr.ScVal.scvVec(v === null
|
|
325
|
+
? [sc.symbol("Empty")]
|
|
326
|
+
: [sc.symbol("Recorded"), UPKEEP_RECEIPT.encode(v)]);
|
|
327
|
+
},
|
|
328
|
+
decode(v) {
|
|
329
|
+
if (Array.isArray(v) && v.length === 1 && v[0] === "Empty")
|
|
330
|
+
return null;
|
|
331
|
+
if (Array.isArray(v) && v.length === 2 && v[0] === "Recorded")
|
|
332
|
+
return UPKEEP_RECEIPT.decode(v[1]);
|
|
333
|
+
throw new FundingError("Malformed upkeep receipt");
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
const UPKEEP_POSITION = model({
|
|
337
|
+
namespace: HEX,
|
|
338
|
+
ownerEpoch: U64,
|
|
339
|
+
controller: ID,
|
|
340
|
+
balance: MONEY,
|
|
341
|
+
spentTotal: MONEY,
|
|
342
|
+
utcDay: U64,
|
|
343
|
+
spentToday: MONEY,
|
|
344
|
+
policyRevision: U64,
|
|
345
|
+
status: enumeration({ paused: "Paused", active: "Active", closed: "Closed" }),
|
|
346
|
+
nonce: U64,
|
|
347
|
+
lastReceipt: UPKEEP_LAST,
|
|
348
|
+
});
|
|
349
|
+
export const upkeepPositionFromNative = (v) => UPKEEP_POSITION.decode(v);
|
|
350
|
+
export const upkeepPolicyFromNative = (v) => UPKEEP_POLICY.decode(v);
|
|
351
|
+
export const upkeepReceiptToScVal = (v) => UPKEEP_RECEIPT.encode(v);
|
|
352
|
+
export const upkeepReceiptFromNative = (v) => UPKEEP_RECEIPT.decode(v);
|
|
353
|
+
export function upkeepPositionId(namespace, epoch) {
|
|
354
|
+
return hex(hash(xdr.ScVal.scvVec([
|
|
355
|
+
sc.symbol("upkeep_v1"),
|
|
356
|
+
HEX.encode(namespace),
|
|
357
|
+
U64.encode(epoch),
|
|
358
|
+
]).toXDR()));
|
|
359
|
+
}
|
|
360
|
+
export function upkeepPolicyToScVal(p) {
|
|
361
|
+
const encoded = UPKEEP_POLICY.encode(p);
|
|
362
|
+
if (p.renewalCap <= 0n ||
|
|
363
|
+
p.dailyCap < p.renewalCap ||
|
|
364
|
+
p.totalCap < p.dailyCap ||
|
|
365
|
+
p.retention < 1 ||
|
|
366
|
+
p.renewBefore < 1 ||
|
|
367
|
+
p.renewBefore >= p.retention ||
|
|
368
|
+
p.quoteLifetime < 1 ||
|
|
369
|
+
p.quoteLifetime > 120 ||
|
|
370
|
+
p.validUntil <= 0n)
|
|
371
|
+
throw new FundingError("Invalid upkeep spending limits or retention");
|
|
372
|
+
return encoded;
|
|
373
|
+
}
|
|
374
|
+
export function storagePlanToScVal(p) {
|
|
375
|
+
exactObject(p, ["registrar", "resolver", "registrarKeys", "resolverKeys"], "storage plan");
|
|
376
|
+
const keys = (v) => {
|
|
377
|
+
if (!Array.isArray(v) || v.length > 32)
|
|
378
|
+
throw new FundingError("At most 32 storage keys per contract");
|
|
379
|
+
const seen = new Set();
|
|
380
|
+
for (const key of v) {
|
|
381
|
+
if (!(key instanceof xdr.ScVal))
|
|
382
|
+
throw new FundingError("Storage keys must be canonical ScVal values");
|
|
383
|
+
const raw = key.toXDR("base64");
|
|
384
|
+
if (seen.has(raw))
|
|
385
|
+
throw new FundingError("Duplicate storage key");
|
|
386
|
+
seen.add(raw);
|
|
387
|
+
}
|
|
388
|
+
return xdr.ScVal.scvVec(v);
|
|
389
|
+
};
|
|
390
|
+
const encoded = struct({
|
|
391
|
+
registrar: C.encode(p.registrar),
|
|
392
|
+
resolver: C.encode(p.resolver),
|
|
393
|
+
registrar_keys: keys(p.registrarKeys),
|
|
394
|
+
resolver_keys: keys(p.resolverKeys),
|
|
395
|
+
});
|
|
396
|
+
if (p.registrar === p.resolver || encoded.toXDR().length > 16384)
|
|
397
|
+
throw new FundingError("Invalid storage plan routes or size");
|
|
398
|
+
return encoded;
|
|
399
|
+
}
|
|
400
|
+
export function upkeepQuoteToScVal(q) {
|
|
401
|
+
exactObject(q, [
|
|
402
|
+
"network",
|
|
403
|
+
"funding",
|
|
404
|
+
"configRevision",
|
|
405
|
+
"fundId",
|
|
406
|
+
"policyRevision",
|
|
407
|
+
"nonce",
|
|
408
|
+
"quoteId",
|
|
409
|
+
"plan",
|
|
410
|
+
"charge",
|
|
411
|
+
"issuedLedger",
|
|
412
|
+
"expiresLedger",
|
|
413
|
+
], "upkeep quote");
|
|
414
|
+
if (q.charge <= 0n ||
|
|
415
|
+
q.expiresLedger <= q.issuedLedger ||
|
|
416
|
+
q.expiresLedger - q.issuedLedger > 120)
|
|
417
|
+
throw new FundingError("Invalid upkeep quote charge or lifetime");
|
|
418
|
+
return struct({
|
|
419
|
+
network: HEX.encode(q.network),
|
|
420
|
+
funding: C.encode(q.funding),
|
|
421
|
+
config_revision: U64.encode(q.configRevision),
|
|
422
|
+
fund_id: HEX.encode(q.fundId),
|
|
423
|
+
policy_revision: U64.encode(q.policyRevision),
|
|
424
|
+
nonce: U64.encode(q.nonce),
|
|
425
|
+
quote_id: HEX.encode(q.quoteId),
|
|
426
|
+
plan: storagePlanToScVal(q.plan),
|
|
427
|
+
charge: MONEY.encode(q.charge),
|
|
428
|
+
issued_ledger: U32.encode(q.issuedLedger),
|
|
429
|
+
expires_ledger: U32.encode(q.expiresLedger),
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
export function executionQuoteToScVal(q) {
|
|
433
|
+
exactObject(q, ["network", "funding", "configRevision", "fundId", "policyRevision", "nonce", "quoteId", "plan", "maxNetworkFee", "issuedLedger", "expiresLedger"], "prepaid execution quote");
|
|
434
|
+
if (q.maxNetworkFee <= 0n || q.expiresLedger <= q.issuedLedger || q.expiresLedger - q.issuedLedger > 120)
|
|
435
|
+
throw new FundingError("Invalid prepaid execution budget or lifetime");
|
|
436
|
+
return struct({ network: HEX.encode(q.network), funding: C.encode(q.funding), config_revision: U64.encode(q.configRevision),
|
|
437
|
+
fund_id: HEX.encode(q.fundId), policy_revision: U64.encode(q.policyRevision), nonce: U64.encode(q.nonce),
|
|
438
|
+
quote_id: HEX.encode(q.quoteId), plan: storagePlanToScVal(q.plan), max_network_fee: MONEY.encode(q.maxNetworkFee),
|
|
439
|
+
issued_ledger: U32.encode(q.issuedLedger), expires_ledger: U32.encode(q.expiresLedger) });
|
|
440
|
+
}
|
package/dist/native-codec.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Local successor-ABI helpers. No deployed defaults are changed by this module. */
|
|
2
2
|
import { Address, StrKey, hash, nativeToScVal, xdr } from "@stellar/stellar-sdk";
|
|
3
|
-
import { decodeMuxedAddress, paymentMemoToScVal, validatePaymentDestination } from "./payment.js";
|
|
3
|
+
import { isMuxedPaymentAddress, decodeMuxedAddress, paymentMemoToScVal, validatePaymentDestination } from "./payment.js";
|
|
4
4
|
export const NATIVE_REGISTRAR_ERRORS = { 1: "AlreadyInitialized", 2: "NotInitialized", 3: "NameTaken", 4: "NameNotFound", 5: "NotReclaimable", 6: "NotTransferable", 7: "AlreadyPermanent", 8: "BatchTooLarge", 9: "NotNamespaceOwner", 10: "BadLabel", 11: "PermanentName", 12: "FiniteTermPolicy", 13: "PermanentlyLocked", 14: "NameExpired", 15: "NoPendingTransfer", 16: "TransferExpired", 17: "StaleTransfer", 18: "InvalidPolicy", 19: "ExpiryOverflow", 20: "InvalidRegistry", 21: "InvalidClaimConfig", 22: "ClaimsNotConfigured", 23: "ClaimsPaused", 24: "StaleClaimPolicy", 25: "ClaimIntentMismatch", 26: "ClaimIntentExpired", 27: "ReservedName", 28: "NameNotReserved", 29: "PublicIssuanceRequired", 30: "WalletClaimLimit", 31: "InvalidEligibility", 32: "ApprovalAllowanceReached", 33: "ApprovalRateReached", 34: "RequestIdConflict", 35: "CounterOverflow", 36: "InvalidNativeBinding", 37: "UnsupportedClaimant", 38: "DuplicateLabel", 39: "RenewalTooEarly", 40: "RenewalLeaseLimit", 41: "DestinationInitializationFailed", 42: "FeeSettlementFailed", 43: "MigrationInProgress", 44: "MigrationAlreadyStarted", 45: "MigrationNotActive", 46: "MigrationMismatch", 47: "MigrationDuplicate", 48: "MigrationIncomplete", 49: "MigrationUnavailable", 50: "MigrationUnsupported", 51: "PermanentLockDisabled", 52: "InvalidRenewalGrace", 53: "SubnameCreationDisabled", 54: "SubnamesSuspended", 55: "SubnameGenerationMismatch", 56: "SubnameIndexUnavailable" };
|
|
5
5
|
export class NativeClaimError extends Error {
|
|
6
6
|
kind;
|
|
@@ -86,7 +86,7 @@ export function struct(fields) {
|
|
|
86
86
|
}
|
|
87
87
|
export function paymentDestinationToScVal(raw) {
|
|
88
88
|
const payment = validatePaymentDestination(raw);
|
|
89
|
-
if (
|
|
89
|
+
if (isMuxedPaymentAddress(payment.address)) {
|
|
90
90
|
const value = decodeMuxedAddress(payment.address);
|
|
91
91
|
return xdr.ScVal.scvVec([sc.symbol("Muxed"), struct({ account: sc.address(value.account), id: sc.u64(BigInt(value.id)) })]);
|
|
92
92
|
}
|
|
@@ -54,6 +54,8 @@ export type NativeWriteOptions = {
|
|
|
54
54
|
/** Persist the public recovery reference before wallet signing/broadcast. Rejecting stops submission. */
|
|
55
55
|
onPrepared?: (prepared: NativePrepared) => void | Promise<void>;
|
|
56
56
|
};
|
|
57
|
+
/** Protocol 29 can fold restoration into an otherwise successful simulation. */
|
|
58
|
+
export declare function refuseNativeRestoration(tx: Transaction): void;
|
|
57
59
|
export declare function prepareNative(context: NativeContext, plan: NativeAuthorizationPlan, options?: NativeWriteOptions): Promise<{
|
|
58
60
|
transaction: Transaction;
|
|
59
61
|
prepared: NativePrepared;
|
package/dist/native-transport.js
CHANGED
|
@@ -66,6 +66,15 @@ export async function requireNative(context, namespace) {
|
|
|
66
66
|
throw new NativeClaimError(`namespace has no supported native claim interface (${capability.reason})`, "unsupported");
|
|
67
67
|
return capability;
|
|
68
68
|
}
|
|
69
|
+
/** Protocol 29 can fold restoration into an otherwise successful simulation. */
|
|
70
|
+
export function refuseNativeRestoration(tx) {
|
|
71
|
+
const envelope = tx.toEnvelope();
|
|
72
|
+
if (envelope.type !== "envelopeTypeTx" || envelope.value.tx.ext.type !== "sorobanData")
|
|
73
|
+
return;
|
|
74
|
+
const ext = envelope.value.tx.ext.value.ext;
|
|
75
|
+
if (ext.type !== "v0" && (ext.type !== "resourceExt" || ext.value.archivedSorobanEntries.length > 0))
|
|
76
|
+
throw new NativeClaimError("This operation would restore archived storage. Review restoration separately before continuing.", "unavailable");
|
|
77
|
+
}
|
|
69
78
|
export async function prepareNative(context, plan, options = {}) {
|
|
70
79
|
const source = address(await context.signer.publicKey(), "account", "transaction signer");
|
|
71
80
|
if (source !== plan.source)
|
|
@@ -97,6 +106,7 @@ export async function prepareNative(context, plan, options = {}) {
|
|
|
97
106
|
const auth = (op.auth ?? []).map(entry => entry.credentials.type === "sorobanCredentialsSourceAccount" ? entry : signed);
|
|
98
107
|
transaction = TransactionBuilder.cloneFrom(transaction, { networkPassphrase: context.passphrase }).clearOperations().addOperation(Operation.invokeHostFunction({ func: op.func, auth })).build();
|
|
99
108
|
transaction = rpc.assembleTransaction(transaction, await simulate(transaction)).build();
|
|
109
|
+
refuseNativeRestoration(transaction);
|
|
100
110
|
validateNativeTransaction(transaction, plan);
|
|
101
111
|
}
|
|
102
112
|
const op = transaction.operations[0];
|
|
@@ -23,12 +23,12 @@ export type ContractUpgradeStatus = {
|
|
|
23
23
|
*/
|
|
24
24
|
export declare const CONTRACT_RELEASES: {
|
|
25
25
|
readonly registrar: {
|
|
26
|
-
readonly latest: "
|
|
27
|
-
readonly superseded: readonly ["42dc5f8b40932617d3f2667e1d26dad3e5e9a62c63de1fde7c016cb67a86c420", "5c8ae72ea7370ccf19af52b9bbc0007362be94464651ccdf68a2bb1f478b6f2c", "61ded153e6311075c1087c5639a5410222e23f7cade239ed2c946150af94b1c5", "26e03cd1d7fbfa46fe442406fb1b88022ec8cd8bd9123bed570f971fce596a14"];
|
|
26
|
+
readonly latest: "13195f1ed62cee15a30fab0d69da09f8537f2c5b62dfb9bbe70d9f3a5a90e498";
|
|
27
|
+
readonly superseded: readonly ["a7b618ac95f5f0b76d4489a9e88d1ce4691611b0d7c53d2d2f8b74b7526e284a", "8e84a35643c6e9c54d161c44102d229e18b50ad16394afea6d73031e21188ede", "4d6d18625f4d890028acb794b24871c5c93f96dc15d94cf3d52cbf9713b07fb1", "42dc5f8b40932617d3f2667e1d26dad3e5e9a62c63de1fde7c016cb67a86c420", "5c8ae72ea7370ccf19af52b9bbc0007362be94464651ccdf68a2bb1f478b6f2c", "61ded153e6311075c1087c5639a5410222e23f7cade239ed2c946150af94b1c5", "26e03cd1d7fbfa46fe442406fb1b88022ec8cd8bd9123bed570f971fce596a14"];
|
|
28
28
|
};
|
|
29
29
|
readonly resolver: {
|
|
30
|
-
readonly latest: "
|
|
31
|
-
readonly superseded: readonly ["cb927ffa19d6b1f3f0b0187048d885e200adb6de3fddd11ee3cfeb59df9c5c48", "351bf4e056b179b578fe2a4ea46f937bfafccc6538c7357ea70021b7038d91ea", "c6dfcd7bbf35943a13406888678334d0938d0fea54939906b00c2dc37b4a9a71", "5e7b4fb8d310d67ed6966a155e748d60be771422b3e8c34bbc14485f98525574"];
|
|
30
|
+
readonly latest: "a77bb9ef08d383691092c71ce6291e2f5ab600320c5afd5686713cc54cbfd95c";
|
|
31
|
+
readonly superseded: readonly ["4ab266de682a38208464e74d52692829ac344ece14f48c4249c3455e6b62a27b", "743559d763d81bf2debe8b70650a2a6dc50d6f5da4cbc0aeccedd3f5559442a7", "218cb1f7f90bd21f81ad4fe8b86f66478c3804b7355257cdc7e37d55d7edd5fb", "941bf87f26cb817f3c6a52a326de855cca8a1dc4477e62a38ec4ffa5c7f43806", "cb927ffa19d6b1f3f0b0187048d885e200adb6de3fddd11ee3cfeb59df9c5c48", "351bf4e056b179b578fe2a4ea46f937bfafccc6538c7357ea70021b7038d91ea", "c6dfcd7bbf35943a13406888678334d0938d0fea54939906b00c2dc37b4a9a71", "5e7b4fb8d310d67ed6966a155e748d60be771422b3e8c34bbc14485f98525574"];
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
/**
|
package/dist/native-upgrades.js
CHANGED
|
@@ -11,8 +11,11 @@ import { expectedNativeCode } from "./native-code-policy.js";
|
|
|
11
11
|
*/
|
|
12
12
|
export const CONTRACT_RELEASES = {
|
|
13
13
|
registrar: {
|
|
14
|
-
latest: "
|
|
14
|
+
latest: "13195f1ed62cee15a30fab0d69da09f8537f2c5b62dfb9bbe70d9f3a5a90e498",
|
|
15
15
|
superseded: [
|
|
16
|
+
"a7b618ac95f5f0b76d4489a9e88d1ce4691611b0d7c53d2d2f8b74b7526e284a",
|
|
17
|
+
"8e84a35643c6e9c54d161c44102d229e18b50ad16394afea6d73031e21188ede",
|
|
18
|
+
"4d6d18625f4d890028acb794b24871c5c93f96dc15d94cf3d52cbf9713b07fb1",
|
|
16
19
|
"42dc5f8b40932617d3f2667e1d26dad3e5e9a62c63de1fde7c016cb67a86c420",
|
|
17
20
|
"5c8ae72ea7370ccf19af52b9bbc0007362be94464651ccdf68a2bb1f478b6f2c",
|
|
18
21
|
"61ded153e6311075c1087c5639a5410222e23f7cade239ed2c946150af94b1c5",
|
|
@@ -20,8 +23,12 @@ export const CONTRACT_RELEASES = {
|
|
|
20
23
|
],
|
|
21
24
|
},
|
|
22
25
|
resolver: {
|
|
23
|
-
latest: "
|
|
26
|
+
latest: "a77bb9ef08d383691092c71ce6291e2f5ab600320c5afd5686713cc54cbfd95c",
|
|
24
27
|
superseded: [
|
|
28
|
+
"4ab266de682a38208464e74d52692829ac344ece14f48c4249c3455e6b62a27b",
|
|
29
|
+
"743559d763d81bf2debe8b70650a2a6dc50d6f5da4cbc0aeccedd3f5559442a7",
|
|
30
|
+
"218cb1f7f90bd21f81ad4fe8b86f66478c3804b7355257cdc7e37d55d7edd5fb",
|
|
31
|
+
"941bf87f26cb817f3c6a52a326de855cca8a1dc4477e62a38ec4ffa5c7f43806",
|
|
25
32
|
"cb927ffa19d6b1f3f0b0187048d885e200adb6de3fddd11ee3cfeb59df9c5c48",
|
|
26
33
|
"351bf4e056b179b578fe2a4ea46f937bfafccc6538c7357ea70021b7038d91ea",
|
|
27
34
|
"c6dfcd7bbf35943a13406888678334d0938d0fea54939906b00c2dc37b4a9a71",
|
package/dist/payment.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export type PaymentDestination = {
|
|
|
18
18
|
address: string;
|
|
19
19
|
memo: PaymentMemo;
|
|
20
20
|
};
|
|
21
|
+
/** M and W are destinations, never standalone signing identities. */
|
|
22
|
+
export declare function isMuxedPaymentAddress(address: string): boolean;
|
|
21
23
|
/** Reject invalid data without normalizing meaningful memo bytes. */
|
|
22
24
|
export declare function validatePaymentDestination(value: unknown): PaymentDestination;
|
|
23
25
|
export declare function encodePaymentRecord(value: PaymentDestination): string;
|
|
@@ -31,7 +33,7 @@ export declare function decodeMuxedAddress(address: string): {
|
|
|
31
33
|
account: string;
|
|
32
34
|
id: string;
|
|
33
35
|
};
|
|
34
|
-
/** Encode
|
|
36
|
+
/** Encode a G or C base and exact decimal u64; numbers are never accepted. */
|
|
35
37
|
export declare function encodeMuxedAddress(account: string, id: string): string;
|
|
36
38
|
/** Strict additive on-chain destination enum; v1 payment structs stay separate. */
|
|
37
39
|
export declare function destinationFromNative(raw: unknown): PaymentDestination;
|
package/dist/payment.js
CHANGED
|
@@ -4,6 +4,10 @@ import { StrKey, nativeToScVal, xdr } from "@stellar/stellar-sdk";
|
|
|
4
4
|
export const PAYMENT_RECORD_KEY = "payment";
|
|
5
5
|
const MAX_U64 = 18446744073709551615n;
|
|
6
6
|
const utf8 = (value) => new TextEncoder().encode(value);
|
|
7
|
+
/** M and W are destinations, never standalone signing identities. */
|
|
8
|
+
export function isMuxedPaymentAddress(address) {
|
|
9
|
+
return StrKey.isValidMed25519PublicKey(address) || StrKey.isValidMuxedContract(address);
|
|
10
|
+
}
|
|
7
11
|
/** Reject invalid data without normalizing meaningful memo bytes. */
|
|
8
12
|
export function validatePaymentDestination(value) {
|
|
9
13
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -11,15 +15,15 @@ export function validatePaymentDestination(value) {
|
|
|
11
15
|
const p = value;
|
|
12
16
|
if (Object.keys(p).sort().join(",") !== "address,memo")
|
|
13
17
|
throw new Error("unexpected payment fields");
|
|
14
|
-
if (typeof p.address !== "string" || (!StrKey.isValidEd25519PublicKey(p.address) && !StrKey.isValidContract(p.address) && !
|
|
15
|
-
throw new Error("payment address must be a valid G, C or
|
|
18
|
+
if (typeof p.address !== "string" || (!StrKey.isValidEd25519PublicKey(p.address) && !StrKey.isValidContract(p.address) && !isMuxedPaymentAddress(p.address)))
|
|
19
|
+
throw new Error("payment address must be a valid G, C, M or W address");
|
|
16
20
|
if (!p.memo || typeof p.memo !== "object" || Array.isArray(p.memo))
|
|
17
21
|
throw new Error("invalid memo");
|
|
18
22
|
const m = p.memo;
|
|
19
23
|
if (m.type === "none") {
|
|
20
24
|
if (Object.keys(m).join(",") !== "type")
|
|
21
25
|
throw new Error("none memo cannot have a value");
|
|
22
|
-
if (
|
|
26
|
+
if (isMuxedPaymentAddress(p.address))
|
|
23
27
|
decodeMuxedAddress(p.address);
|
|
24
28
|
return { address: p.address, memo: { type: "none" } };
|
|
25
29
|
}
|
|
@@ -47,7 +51,7 @@ export function validatePaymentDestination(value) {
|
|
|
47
51
|
}
|
|
48
52
|
export function encodePaymentRecord(value) {
|
|
49
53
|
const p = validatePaymentDestination(value);
|
|
50
|
-
if (
|
|
54
|
+
if (isMuxedPaymentAddress(p.address)) {
|
|
51
55
|
const muxed = decodeMuxedAddress(p.address);
|
|
52
56
|
return `2|${muxed.account}|muxed|${muxed.id}`;
|
|
53
57
|
}
|
|
@@ -108,30 +112,32 @@ export function paymentFromNative(raw) {
|
|
|
108
112
|
}
|
|
109
113
|
/** Decode a full muxed destination without treating its operation ID as a memo. */
|
|
110
114
|
export function decodeMuxedAddress(address) {
|
|
111
|
-
if (typeof address !== "string" || !
|
|
112
|
-
throw new Error("invalid muxed M address");
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
+
if (typeof address !== "string" || !isMuxedPaymentAddress(address))
|
|
116
|
+
throw new Error("invalid muxed M or W address");
|
|
117
|
+
const contract = StrKey.isValidMuxedContract(address);
|
|
118
|
+
const raw = contract ? StrKey.decodeMuxedContract(address) : StrKey.decodeMed25519PublicKey(address);
|
|
119
|
+
if (raw.length !== 40 || (contract ? StrKey.encodeMuxedContract(raw) : StrKey.encodeMed25519PublicKey(raw)) !== address)
|
|
115
120
|
throw new Error("noncanonical muxed address");
|
|
116
121
|
let id = 0n;
|
|
117
122
|
for (const byte of raw.subarray(32))
|
|
118
123
|
id = (id << 8n) | BigInt(byte);
|
|
119
|
-
return { account: StrKey.encodeEd25519PublicKey(raw.subarray(0, 32)), id: id.toString() };
|
|
124
|
+
return { account: contract ? StrKey.encodeContract(raw.subarray(0, 32)) : StrKey.encodeEd25519PublicKey(raw.subarray(0, 32)), id: id.toString() };
|
|
120
125
|
}
|
|
121
|
-
/** Encode
|
|
126
|
+
/** Encode a G or C base and exact decimal u64; numbers are never accepted. */
|
|
122
127
|
export function encodeMuxedAddress(account, id) {
|
|
123
|
-
if (typeof account !== "string" || !StrKey.isValidEd25519PublicKey(account))
|
|
124
|
-
throw new Error("muxed base must be a G account");
|
|
128
|
+
if (typeof account !== "string" || (!StrKey.isValidEd25519PublicKey(account) && !StrKey.isValidContract(account)))
|
|
129
|
+
throw new Error("muxed base must be a G account or C contract");
|
|
125
130
|
if (typeof id !== "string" || !/^(0|[1-9][0-9]{0,19})$/.test(id) || BigInt(id) > MAX_U64)
|
|
126
131
|
throw new Error("muxed ID must be canonical decimal u64");
|
|
127
132
|
const raw = new Uint8Array(40);
|
|
128
|
-
|
|
133
|
+
const contract = StrKey.isValidContract(account);
|
|
134
|
+
raw.set(contract ? StrKey.decodeContract(account) : StrKey.decodeEd25519PublicKey(account));
|
|
129
135
|
let value = BigInt(id);
|
|
130
136
|
for (let i = 39; i >= 32; i--) {
|
|
131
137
|
raw[i] = Number(value & 255n);
|
|
132
138
|
value >>= 8n;
|
|
133
139
|
}
|
|
134
|
-
return StrKey.encodeMed25519PublicKey(raw);
|
|
140
|
+
return contract ? StrKey.encodeMuxedContract(raw) : StrKey.encodeMed25519PublicKey(raw);
|
|
135
141
|
}
|
|
136
142
|
/** Strict additive on-chain destination enum; v1 payment structs stay separate. */
|
|
137
143
|
export function destinationFromNative(raw) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sorandomains/owner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Run your Soran namespace on Stellar: configure claims, issue names and manage namespace policies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"typecheck": "tsc --noEmit",
|
|
17
17
|
"prepublishOnly": "tsc",
|
|
18
18
|
"check:browser": "esbuild dist/index.js --bundle --platform=browser --external:@stellar/stellar-sdk --outfile=browser-check.js --legal-comments=none --log-level=error && node -e \"const fs=require('fs');const s=fs.readFileSync('browser-check.js','utf8');fs.unlinkSync('browser-check.js');if(/\\bBuffer\\b/.test(s)){console.error('FAIL: bare Buffer reference in browser bundle');process.exit(1)}console.log('browser bundle clean')\"",
|
|
19
|
-
"test": "node --import tsx test/decode-failure.test.mts && node --import tsx --test test/integration.test.mts test/hardening.test.mts test/error-codes.test.mts test/nested-authorization.test.mts test/native-*.test.mts && node --import tsx --test test/funding.test.mts test/funding-history.test.mts test/funding-bounds.test.mts && node --import tsx --test test/multichain.test.mts && node --import tsx --test test/subnames.test.mts",
|
|
19
|
+
"test": "node --import tsx test/decode-failure.test.mts && node --import tsx --test test/integration.test.mts test/hardening.test.mts test/error-codes.test.mts test/nested-authorization.test.mts test/native-*.test.mts && node --import tsx --test test/funding.test.mts test/funding-history.test.mts test/funding-bounds.test.mts test/funding-upkeep.test.mts test/funding-prepaid.test.mts && node --import tsx --test test/multichain.test.mts && node --import tsx --test test/subnames.test.mts",
|
|
20
20
|
"test:hardening": "node --import tsx --test test/hardening.test.mts"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"issuance"
|
|
39
39
|
],
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@stellar/stellar-sdk": ">=17 <18"
|
|
41
|
+
"@stellar/stellar-sdk": ">=17.4.0 <18"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@stellar/stellar-sdk": "
|
|
44
|
+
"@stellar/stellar-sdk": "17.4.0",
|
|
45
45
|
"@types/node": "^26.6.3",
|
|
46
46
|
"esbuild": "^0.28.2",
|
|
47
47
|
"tsx": "^4.19.2",
|