@sorandomains/owner 0.8.0 → 0.10.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 CHANGED
@@ -1,14 +1,14 @@
1
1
  # @sorandomains/owner
2
2
 
3
3
 
4
- > Native claim testnet deployment verified at ledger 4534629 on 6 September 2026 (12:12 UTC). See the
4
+ > Governed testnet migration sealed at ledger 4604192 on 10 September 2026. See the
5
5
  > [release status](https://docs.soran.domains/reference/release-status) for package and service availability.
6
6
 
7
7
 
8
8
  Version 0.8.0 targets Stellar SDK17 (`>=17 <18`). ASCII names
9
9
  and labels are validated before lowercase normalization; Unicode lookalikes are
10
10
  rejected. Writes continue to target the owning Registry/Registrar/Resolver. Universal
11
- Lookup is the read entry point in `@sorandomains/lookup` 0.8.0.
11
+ Lookup is the read entry point in `@sorandomains/lookup` 0.10.0.
12
12
 
13
13
  Ownership, issuer, holder and treasury inputs remain G/C account or contract
14
14
  addresses. Muxed M addresses are payment destinations only: issue the name to its
@@ -91,11 +91,11 @@ Read the [native claim APIs, security boundaries and complete signup flow](https
91
91
 
92
92
  ## Verified testnet deployment
93
93
 
94
- Verified on 6 September 2026 at ledger **4534629** (12:12 UTC). Network passphrase: `Test SDF Network ; September 2015`.
94
+ See the [deployment manifest](../../deploy/testnet/deployment.json) for confirmed code hashes, transaction receipts and verification scope. Network passphrase: `Test SDF Network ; September 2015`.
95
95
 
96
96
  | Contract | Address |
97
97
  |---|---|
98
- | Registry | `CBSORANPM664QXYMYRZKLQDQE2TXFSK4GBMC6EIRSRTAUZRZCRZRFNMK` |
98
+ | Registry | `CCSORANDPQINYOYB5SVO45WJP2LBBYKC72HHUIRVXB4J6RUZKDAUW7G4` |
99
99
 
100
100
  Mainnet has no deployment preset. Custom networks must supply their own verified
101
101
  addresses. Universal Lookup upgrades remain immediately executable; an address
@@ -0,0 +1,22 @@
1
+ import { xdr } from "@stellar/stellar-sdk";
2
+ /** The wallet defines its signature format and retains custody of its keys.
3
+ * Soran supplies the exact reviewed invocation, network, nonce and expiry.
4
+ * A separate G-account TxSigner submits the resulting transaction.
5
+ */
6
+ export type ContractWallet = {
7
+ address: string;
8
+ signAuthorization(request: {
9
+ address: string;
10
+ networkPassphrase: string;
11
+ authorizationEntryXdr: string;
12
+ signaturePayload: Uint8Array;
13
+ }): Promise<xdr.ScVal>;
14
+ };
15
+ export type ContractAuthorization = {
16
+ account: string;
17
+ latestLedger: number;
18
+ maxExpirationLedger: number;
19
+ };
20
+ export declare function validateContractAuthorization(entry: xdr.SorobanAuthorizationEntry, expected: ContractAuthorization, signed: boolean): void;
21
+ /** Invocation scope must be validated by the caller before calling this helper. */
22
+ export declare function signContractAuthorization(unsigned: xdr.SorobanAuthorizationEntry, expected: ContractAuthorization, wallet: ContractWallet, networkPassphrase: string): Promise<xdr.SorobanAuthorizationEntry>;
@@ -0,0 +1,55 @@
1
+ import { Address, buildAuthorizationEntryPreimage, hash, xdr } from "@stellar/stellar-sdk";
2
+ import { NativeClaimError, address, u32 } from "./native-codec.js";
3
+ export function validateContractAuthorization(entry, expected, signed) {
4
+ address(expected.account, "contract", "contract wallet");
5
+ u32(expected.latestLedger, "latest ledger");
6
+ u32(expected.maxExpirationLedger, "maximum authorization ledger");
7
+ if (expected.latestLedger < 1 || expected.maxExpirationLedger <= expected.latestLedger)
8
+ throw new NativeClaimError("contract authorization ledger window is invalid", "authorization");
9
+ if (entry.credentials.type !== "sorobanCredentialsAddress" ||
10
+ Address.fromScAddress(entry.credentials.address.address).toString() !== expected.account)
11
+ throw new NativeClaimError("authorization does not belong to the selected contract wallet", "authorization");
12
+ const c = entry.credentials.address;
13
+ if (signed) {
14
+ if (c.signatureExpirationLedger <= expected.latestLedger || c.signatureExpirationLedger > expected.maxExpirationLedger ||
15
+ c.signature.toXDR().length > 16384)
16
+ throw new NativeClaimError("contract authorization is expired or outside the reviewed limits", "authorization");
17
+ // A contract can legitimately accept any ScVal, including Void. Only an
18
+ // enforcing simulation and the host's __check_auth establish validity.
19
+ }
20
+ else if (c.signature.type !== "scvVoid" && !(c.signature.type === "scvVec" && !c.signature.vec?.length)) {
21
+ throw new NativeClaimError("refusing to sign an already signed contract authorization", "authorization");
22
+ }
23
+ }
24
+ /** Invocation scope must be validated by the caller before calling this helper. */
25
+ export async function signContractAuthorization(unsigned, expected, wallet, networkPassphrase) {
26
+ validateContractAuthorization(unsigned, expected, false);
27
+ if (wallet.address !== expected.account || !networkPassphrase)
28
+ throw new NativeClaimError("contract wallet or network differs from the reviewed authorization", "authorization");
29
+ // Round-trip before yielding to a wallet. Its callback receives no mutable
30
+ // invocation objects and can supply only the contract-defined signature.
31
+ const entry = xdr.SorobanAuthorizationEntry.fromXDR(unsigned.toXDR());
32
+ const c = entry.credentials;
33
+ if (c.type !== "sorobanCredentialsAddress")
34
+ throw new NativeClaimError("missing contract credential");
35
+ const preimage = buildAuthorizationEntryPreimage(entry, expected.maxExpirationLedger, networkPassphrase);
36
+ const credentials = new xdr.SorobanAddressCredentials({
37
+ ...c.address, signatureExpirationLedger: expected.maxExpirationLedger, signature: xdr.ScVal.scvVoid(),
38
+ });
39
+ const signingEntry = new xdr.SorobanAuthorizationEntry({
40
+ credentials: xdr.SorobanCredentials.sorobanCredentialsAddress(credentials), rootInvocation: entry.rootInvocation,
41
+ });
42
+ const signature = await wallet.signAuthorization({
43
+ address: expected.account, networkPassphrase, authorizationEntryXdr: signingEntry.toXDR("base64"),
44
+ signaturePayload: Uint8Array.from(hash(preimage.toXDR())),
45
+ });
46
+ if (wallet.address !== expected.account)
47
+ throw new NativeClaimError("contract wallet changed while authorizing", "authorization");
48
+ const signed = new xdr.SorobanAuthorizationEntry({
49
+ credentials: xdr.SorobanCredentials.sorobanCredentialsAddress(new xdr.SorobanAddressCredentials({
50
+ ...credentials, signature: xdr.ScVal.fromXDR(signature.toXDR()),
51
+ })), rootInvocation: entry.rootInvocation,
52
+ });
53
+ validateContractAuthorization(signed, expected, true);
54
+ return signed;
55
+ }
@@ -0,0 +1,25 @@
1
+ import { rpc } from "@stellar/stellar-sdk";
2
+ import { type SponsorQuote } from "./funding-types.js";
3
+ /** Receipt evidence is retained for audit; its bytes alone are not a chain-inclusion proof. */
4
+ export type FundingReceipt = {
5
+ transactionHash: string;
6
+ envelopeHash: string;
7
+ envelopeXdr: string;
8
+ resultXdr: string;
9
+ ledger: number;
10
+ observedLedger: number;
11
+ status: "success" | "failed";
12
+ provider: "stellar-rpc" | "sdf-testnet-horizon";
13
+ };
14
+ export interface SponsorHistory {
15
+ find(hash: string, quote: SponsorQuote, latestLedger: number, passphrase: string): Promise<FundingReceipt | null>;
16
+ }
17
+ /** Input must come from the caller's trusted chain provider, never a pasted/API receipt. */
18
+ export declare function verifySponsorHistory(value: unknown, quote: SponsorQuote, expectedHash: string, latestLedger: number, passphrase: string, provider?: FundingReceipt["provider"]): FundingReceipt | null;
19
+ export declare function sponsorRpcReceipt(result: rpc.Api.GetSuccessfulTransactionResponse | rpc.Api.GetFailedTransactionResponse, quote: SponsorQuote, hash: string, passphrase: string): FundingReceipt;
20
+ /** The fixed provider is trusted for canonical inclusion, just as Stellar RPC is. */
21
+ export declare class TestnetSponsorHistory implements SponsorHistory {
22
+ private request;
23
+ constructor(request?: typeof fetch);
24
+ find(hash: string, quote: SponsorQuote, latestLedger: number, passphrase: string): Promise<FundingReceipt | null>;
25
+ }
@@ -0,0 +1,124 @@
1
+ import { Address, FeeBumpTransaction, Networks, Transaction, TransactionBuilder, xdr, hash, } from "@stellar/stellar-sdk";
2
+ import { hex } from "./native-codec.js";
3
+ import { FundingError, sponsorQuoteToScVal, } from "./funding-types.js";
4
+ /** Input must come from the caller's trusted chain provider, never a pasted/API receipt. */
5
+ export function verifySponsorHistory(value, quote, expectedHash, latestLedger, passphrase, provider = "sdf-testnet-horizon") {
6
+ const r = value;
7
+ if (passphrase !== Networks.TESTNET ||
8
+ quote.network !== hex(hash(new TextEncoder().encode(passphrase))) ||
9
+ !/^[a-f0-9]{64}$/.test(expectedHash) ||
10
+ !Number.isInteger(latestLedger) ||
11
+ latestLedger < 1 ||
12
+ latestLedger > 0xffff_ffff ||
13
+ !r ||
14
+ typeof r !== "object" ||
15
+ typeof r.hash !== "string" ||
16
+ !Number.isInteger(r.ledger) ||
17
+ r.ledger < quote.issuedLedger ||
18
+ r.ledger > latestLedger ||
19
+ typeof r.successful !== "boolean" ||
20
+ typeof r.envelope_xdr !== "string" ||
21
+ r.envelope_xdr.length > 200000 ||
22
+ typeof r.result_xdr !== "string" ||
23
+ r.result_xdr.length > 200000)
24
+ return null;
25
+ try {
26
+ const envelope = TransactionBuilder.fromXDR(r.envelope_xdr, passphrase);
27
+ const tx = envelope instanceof FeeBumpTransaction
28
+ ? envelope.innerTransaction
29
+ : envelope;
30
+ if (!(tx instanceof Transaction) ||
31
+ hex(envelope.hash()) !== r.hash ||
32
+ hex(tx.hash()) !== expectedHash)
33
+ return null;
34
+ const op = tx.operations[0];
35
+ if (tx.source !== quote.relayer ||
36
+ tx.operations.length !== 1 ||
37
+ op.type !== "invokeHostFunction" ||
38
+ (op.source && op.source !== quote.relayer) ||
39
+ op.func.type !== "hostFunctionTypeInvokeContract" ||
40
+ Address.fromScAddress(op.func.invokeContract.contractAddress).toString() !== quote.funding ||
41
+ op.func.invokeContract.functionName.toString() !==
42
+ "sponsor_" + quote.action ||
43
+ op.func.invokeContract.args[0]?.toXDR("base64") !==
44
+ sponsorQuoteToScVal(quote).toXDR("base64"))
45
+ return null;
46
+ const result = xdr.TransactionResult.fromXDR(r.result_xdr, "base64").result;
47
+ let outcome;
48
+ if (envelope instanceof FeeBumpTransaction) {
49
+ if (result.type !== "txFeeBumpInnerSuccess" &&
50
+ result.type !== "txFeeBumpInnerFailed")
51
+ return null;
52
+ if (hex(result.innerResultPair.transactionHash.value) !== expectedHash)
53
+ return null;
54
+ outcome = result.innerResultPair.result.result.type;
55
+ if ((result.type === "txFeeBumpInnerSuccess") !==
56
+ (outcome === "txSuccess"))
57
+ return null;
58
+ }
59
+ else
60
+ outcome = result.type;
61
+ if (!["txSuccess", "txFailed"].includes(outcome) ||
62
+ r.successful !== (outcome === "txSuccess"))
63
+ return null;
64
+ return {
65
+ transactionHash: expectedHash,
66
+ envelopeHash: r.hash,
67
+ envelopeXdr: r.envelope_xdr,
68
+ resultXdr: r.result_xdr,
69
+ ledger: r.ledger,
70
+ observedLedger: latestLedger,
71
+ status: r.successful ? "success" : "failed",
72
+ provider,
73
+ };
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ export function sponsorRpcReceipt(result, quote, hash, passphrase) {
80
+ try {
81
+ const envelope = TransactionBuilder.fromXDR(result.envelopeXdr, passphrase);
82
+ if (![hash, hex(envelope.hash())].includes(result.txHash))
83
+ throw Error();
84
+ const receipt = verifySponsorHistory({
85
+ hash: hex(envelope.hash()),
86
+ ledger: result.ledger,
87
+ successful: result.status === "SUCCESS",
88
+ envelope_xdr: envelope.toXDR(),
89
+ result_xdr: result.resultXdr.toXDR("base64"),
90
+ }, quote, hash, result.latestLedger, passphrase, "stellar-rpc");
91
+ if (receipt)
92
+ return receipt;
93
+ }
94
+ catch {
95
+ /* malformed or unrelated terminal data stays uncertain */
96
+ }
97
+ throw new FundingError("Receipt does not prove the reviewed sponsorship transaction", "pending", hash);
98
+ }
99
+ /** The fixed provider is trusted for canonical inclusion, just as Stellar RPC is. */
100
+ export class TestnetSponsorHistory {
101
+ request;
102
+ constructor(request = (input, init) => globalThis.fetch(input, init)) {
103
+ this.request = request;
104
+ }
105
+ async find(hash, quote, latestLedger, passphrase) {
106
+ if (passphrase !== Networks.TESTNET || !/^[a-f0-9]{64}$/.test(hash))
107
+ throw new FundingError("Invalid sponsorship history request", "unavailable");
108
+ const options = {
109
+ credentials: "omit",
110
+ redirect: "error",
111
+ cache: "no-store",
112
+ signal: AbortSignal.timeout(10000),
113
+ };
114
+ const response = await this.request(`https://horizon-testnet.stellar.org/transactions/${hash}`, options);
115
+ if (response.status === 404)
116
+ return null;
117
+ if (!response.ok)
118
+ throw new FundingError("Sponsorship history unavailable; preserve the quote reference", "unavailable");
119
+ const body = await response.text();
120
+ if (body.length > 600000)
121
+ throw new FundingError("Sponsorship history response is too large", "unavailable");
122
+ return verifySponsorHistory(JSON.parse(body), quote, hash, latestLedger, passphrase);
123
+ }
124
+ }
@@ -0,0 +1,45 @@
1
+ import { FundingReader, type FundingOptions } from "./funding-reader.js";
2
+ import { type SponsorPolicy } from "./funding-types.js";
3
+ import { type NativeSigner, type NativeWriteOptions } from "./native-transport.js";
4
+ /** Direct owner operations work even when the sponsorship service is offline. */
5
+ export declare class SoranFunding extends FundingReader {
6
+ private context;
7
+ private tail;
8
+ constructor(options: FundingOptions & {
9
+ signer: NativeSigner;
10
+ maxNetworkFeeStroops: bigint;
11
+ });
12
+ private serialize;
13
+ private write;
14
+ open(namespace: string, options?: NativeWriteOptions): Promise<{
15
+ hash: string;
16
+ ledger: number;
17
+ value: unknown;
18
+ }>;
19
+ private controller;
20
+ deposit(id: string, amount: bigint, options?: NativeWriteOptions): Promise<{
21
+ hash: string;
22
+ ledger: number;
23
+ value: unknown;
24
+ }>;
25
+ setPolicy(id: string, policy: SponsorPolicy, options?: NativeWriteOptions): Promise<{
26
+ hash: string;
27
+ ledger: number;
28
+ value: unknown;
29
+ }>;
30
+ setPaused(id: string, paused: boolean, options?: NativeWriteOptions): Promise<{
31
+ hash: string;
32
+ ledger: number;
33
+ value: unknown;
34
+ }>;
35
+ withdraw(id: string, amount: bigint, recipient: string, options?: NativeWriteOptions): Promise<{
36
+ hash: string;
37
+ ledger: number;
38
+ value: unknown;
39
+ }>;
40
+ close(id: string, options?: NativeWriteOptions): Promise<{
41
+ hash: string;
42
+ ledger: number;
43
+ value: unknown;
44
+ }>;
45
+ }
@@ -0,0 +1,97 @@
1
+ import { Asset } from "@stellar/stellar-sdk";
2
+ import { FundingReader } from "./funding-reader.js";
3
+ import { FundingError, sponsorPolicyToScVal, } from "./funding-types.js";
4
+ import { address, namespaceNode, sc, unhex } from "./native-codec.js";
5
+ import { sendNative, } from "./native-transport.js";
6
+ /** Direct owner operations work even when the sponsorship service is offline. */
7
+ export class SoranFunding extends FundingReader {
8
+ context;
9
+ tail = Promise.resolve();
10
+ constructor(options) {
11
+ super(options);
12
+ if (options.maxNetworkFeeStroops <= 0n)
13
+ throw new FundingError("Choose a positive network fee limit");
14
+ const context = {
15
+ registryId: this.registryId,
16
+ passphrase: this.passphrase,
17
+ server: this.server,
18
+ signer: options.signer,
19
+ fee: "100",
20
+ timeoutSecs: 60,
21
+ maxFeeStroops: options.maxNetworkFeeStroops,
22
+ read: async (c, m, a) => (await this.read(c, m, a)).value,
23
+ readWithLedger: (c, m, a) => this.read(c, m, a),
24
+ serialize: (work) => this.serialize(work),
25
+ };
26
+ this.context = context;
27
+ }
28
+ serialize(work) {
29
+ const next = this.tail.then(work, work);
30
+ this.tail = next.catch(() => undefined);
31
+ return next;
32
+ }
33
+ async write(method, args, controller, options, children = []) {
34
+ const source = address(await this.context.signer.publicKey(), "account", "owner wallet");
35
+ if (source !== controller)
36
+ throw new FundingError("Connect the funding position's controller wallet", "authorization");
37
+ await this.configuration();
38
+ return this.serialize(() => sendNative(this.context, {
39
+ source,
40
+ contract: this.fundingId,
41
+ method,
42
+ args,
43
+ sourceInvocation: {
44
+ contract: this.fundingId,
45
+ method,
46
+ args,
47
+ children,
48
+ },
49
+ maxFeeStroops: this.context.maxFeeStroops,
50
+ }, options));
51
+ }
52
+ async open(namespace, options = {}) {
53
+ const node = sc.bytes(namespaceNode(namespace));
54
+ const owner = address((await this.read(this.registryId, "owner_of", [node])).value, "identity", "namespace owner");
55
+ return this.write("open_fund", [node], owner, options);
56
+ }
57
+ async controller(id) {
58
+ const f = await this.position(id);
59
+ if (!f)
60
+ throw new FundingError("Funding position does not exist");
61
+ return f.controller;
62
+ }
63
+ async deposit(id, amount, options = {}) {
64
+ if (amount <= 0n)
65
+ throw new FundingError("Deposit must be positive");
66
+ const controller = await this.controller(id);
67
+ return this.write("deposit", [sc.bytes(unhex(id)), sc.i128(amount)], controller, options, [
68
+ {
69
+ contract: Asset.native().contractId(this.passphrase),
70
+ method: "transfer",
71
+ args: [
72
+ sc.address(controller),
73
+ sc.address(this.fundingId),
74
+ sc.i128(amount),
75
+ ],
76
+ },
77
+ ]);
78
+ }
79
+ async setPolicy(id, policy, options = {}) {
80
+ return this.write("set_policy", [sc.bytes(unhex(id)), sponsorPolicyToScVal(policy)], await this.controller(id), options);
81
+ }
82
+ async setPaused(id, paused, options = {}) {
83
+ return this.write("set_paused", [sc.bytes(unhex(id)), sc.bool(paused)], await this.controller(id), options);
84
+ }
85
+ async withdraw(id, amount, recipient, options = {}) {
86
+ if (amount <= 0n || recipient === this.fundingId)
87
+ throw new FundingError("Invalid withdrawal");
88
+ return this.write("withdraw", [
89
+ sc.bytes(unhex(id)),
90
+ sc.i128(amount),
91
+ sc.address(address(recipient, "identity", "withdrawal recipient")),
92
+ ], await this.controller(id), options);
93
+ }
94
+ async close(id, options = {}) {
95
+ return this.write("close_fund", [sc.bytes(unhex(id))], await this.controller(id), options);
96
+ }
97
+ }
@@ -0,0 +1,43 @@
1
+ import { rpc, xdr } from "@stellar/stellar-sdk";
2
+ import { type SponsorQuote } from "./funding-types.js";
3
+ export type FundingOptions = {
4
+ fundingId: string;
5
+ registryId: string;
6
+ lookupId: string;
7
+ primaryId: string;
8
+ rpcUrl?: string;
9
+ networkPassphrase?: string;
10
+ server?: rpc.Server;
11
+ };
12
+ export declare class FundingReader {
13
+ readonly fundingId: string;
14
+ readonly registryId: string;
15
+ readonly lookupId: string;
16
+ readonly primaryId: string;
17
+ readonly passphrase: string;
18
+ readonly server: rpc.Server;
19
+ readonly networkId: string;
20
+ constructor(options: FundingOptions);
21
+ read(contract: string, method: string, args: xdr.ScVal[], minimumLedger?: number): Promise<{
22
+ value: unknown;
23
+ ledger: number;
24
+ }>;
25
+ configuration(): Promise<{
26
+ ledger: number;
27
+ registry: string;
28
+ nativeAsset: string;
29
+ lookup: string;
30
+ primary: string;
31
+ governance: string;
32
+ paused: boolean;
33
+ revision: bigint;
34
+ }>;
35
+ currentPosition(namespace: string): Promise<{
36
+ id: string;
37
+ position: import("./funding-types.js").FundingPosition | null;
38
+ }>;
39
+ position(id: string): Promise<import("./funding-types.js").FundingPosition | null>;
40
+ policy(id: string): Promise<import("./funding-types.js").SponsorPolicy | null>;
41
+ actorState(id: string, actor: string): Promise<import("./funding-types.js").SponsorActorState>;
42
+ validateQuote(quote: SponsorQuote): Promise<void>;
43
+ }
@@ -0,0 +1,98 @@
1
+ import { Account, Asset, Contract, Networks, StrKey, TransactionBuilder, hash, rpc, scValToNative, } from "@stellar/stellar-sdk";
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";
4
+ export class FundingReader {
5
+ fundingId;
6
+ registryId;
7
+ lookupId;
8
+ primaryId;
9
+ passphrase;
10
+ server;
11
+ networkId;
12
+ constructor(options) {
13
+ this.fundingId = address(options.fundingId, "contract", "funding deployment");
14
+ this.registryId = address(options.registryId, "contract", "Registry");
15
+ this.lookupId = address(options.lookupId, "contract", "Lookup");
16
+ this.primaryId = address(options.primaryId, "contract", "Primary");
17
+ this.passphrase = options.networkPassphrase ?? Networks.TESTNET;
18
+ if (this.passphrase !== Networks.TESTNET)
19
+ throw new FundingError("Funding V1 is currently restricted to testnet");
20
+ this.networkId = hex(hash(utf8(this.passphrase)));
21
+ this.server =
22
+ options.server ??
23
+ new rpc.Server(options.rpcUrl ?? "https://soroban-testnet.stellar.org");
24
+ }
25
+ async read(contract, method, args, minimumLedger = 1) {
26
+ const source = new Account(StrKey.encodeEd25519PublicKey(new Uint8Array(32)), "0");
27
+ const tx = new TransactionBuilder(source, {
28
+ fee: "100",
29
+ networkPassphrase: this.passphrase,
30
+ })
31
+ .addOperation(new Contract(contract).call(method, ...args))
32
+ .setTimeout(30)
33
+ .build();
34
+ const result = await this.server.simulateTransaction(tx, undefined, undefined, false);
35
+ if (!rpc.Api.isSimulationSuccess(result) ||
36
+ !result.result ||
37
+ !Number.isSafeInteger(result.latestLedger) ||
38
+ result.latestLedger < minimumLedger)
39
+ throw new FundingError("Cannot verify funding contract state: " +
40
+ ("error" in result ? result.error : "missing or stale response"), "unavailable");
41
+ return {
42
+ value: scValToNative(result.result.retval),
43
+ ledger: result.latestLedger,
44
+ };
45
+ }
46
+ async configuration() {
47
+ const version = await this.read(this.fundingId, "version", []);
48
+ if (version.value !== 1)
49
+ throw new FundingError("Unsupported Funding contract version", "unavailable");
50
+ const r = await this.read(this.fundingId, "configuration", [], version.ledger);
51
+ const c = fundingConfigurationFromNative(r.value);
52
+ if (c.registry !== this.registryId ||
53
+ c.lookup !== this.lookupId ||
54
+ c.primary !== this.primaryId ||
55
+ c.nativeAsset !== Asset.native().contractId(this.passphrase))
56
+ throw new FundingError("Funding contract anchors differ from the configured deployment", "authorization");
57
+ return { ...c, ledger: r.ledger };
58
+ }
59
+ async currentPosition(namespace) {
60
+ await this.configuration();
61
+ const node = hex(namespaceNode(namespace));
62
+ const epoch = await this.read(this.registryId, "owner_epoch", [
63
+ sc.bytes(unhex(node)),
64
+ ]);
65
+ if (typeof epoch.value !== "bigint" || epoch.value < 1n)
66
+ throw new FundingError("Namespace ownership epoch unavailable", "unavailable");
67
+ const id = fundingPositionId(node, epoch.value);
68
+ return { id, position: await this.position(id) };
69
+ }
70
+ async position(id) {
71
+ const r = await this.read(this.fundingId, "fund", [sc.bytes(unhex(id))]);
72
+ if (r.value === null || r.value === undefined)
73
+ return null;
74
+ const position = fundingPositionFromNative(r.value);
75
+ if (fundingPositionId(position.namespace, position.ownerEpoch) !== id)
76
+ throw new FundingError("Returned funding position has a different namespace or ownership epoch", "unavailable");
77
+ return position;
78
+ }
79
+ async policy(id) {
80
+ const r = await this.read(this.fundingId, "policy", [sc.bytes(unhex(id))]);
81
+ return r.value === null || r.value === undefined
82
+ ? null
83
+ : sponsorPolicyFromNative(r.value);
84
+ }
85
+ async actorState(id, actor) {
86
+ const r = await this.read(this.fundingId, "actor_state", [
87
+ sc.bytes(unhex(id)),
88
+ sc.address(address(actor, "identity", "actor")),
89
+ ]);
90
+ return sponsorActorFromNative(r.value);
91
+ }
92
+ async validateQuote(quote) {
93
+ if (quote.funding !== this.fundingId || quote.network !== this.networkId)
94
+ throw new FundingError("Quote belongs to another deployment", "authorization");
95
+ await this.configuration();
96
+ await this.read(this.fundingId, "quote_status", [sponsorQuoteToScVal(quote)], quote.issuedLedger);
97
+ }
98
+ }
@@ -0,0 +1,44 @@
1
+ import { type SponsorHistory } from "./funding-history.js";
2
+ import { type ClaimIntent } from "./native-types.js";
3
+ import { type SponsorQuote, type SponsoredAction, type SponsoredIdentityIntent } from "./funding-types.js";
4
+ import { SoranSponsorship, type SponsorRequest } from "./funding-user.js";
5
+ /** HTTP transports offers and status. The contract/RPC checks still authorize spending. */
6
+ export declare class FundingServiceClient {
7
+ private client;
8
+ private request;
9
+ private history;
10
+ private base;
11
+ constructor(baseUrl: string, client: SoranSponsorship, request?: typeof fetch, history?: SponsorHistory);
12
+ private call;
13
+ quote(action: SponsoredAction, intent: ClaimIntent | SponsoredIdentityIntent, maxSponsorCharge: bigint, proof?: readonly string[]): Promise<{
14
+ request: {
15
+ quote: SponsorQuote;
16
+ intent: ClaimIntent | SponsoredIdentityIntent;
17
+ proof: readonly string[];
18
+ };
19
+ transactionXdr: any;
20
+ }>;
21
+ /** Re-estimate with the previous, valid wallet authorization. This never
22
+ * submits; review the new charge and call authorize again before submit. */
23
+ refreshQuote(request: SponsorRequest, authorizedTransactionXdr: string, maxSponsorCharge: bigint): Promise<{
24
+ request: {
25
+ quote: SponsorQuote;
26
+ intent: ClaimIntent | SponsoredIdentityIntent;
27
+ proof: readonly string[];
28
+ };
29
+ transactionXdr: any;
30
+ }>;
31
+ private reviewOffer;
32
+ submit(quote: SponsorQuote, authorizedTransactionXdr: string): Promise<{
33
+ id: string;
34
+ status: "quoted" | "dispatching" | "pending" | "unknown" | "success" | "failed" | "expired";
35
+ transactionHash: string | null;
36
+ }>;
37
+ status(quote: SponsorQuote): Promise<{
38
+ id: string;
39
+ status: "quoted" | "dispatching" | "pending" | "unknown" | "success" | "failed" | "expired";
40
+ transactionHash: string | null;
41
+ }>;
42
+ private id;
43
+ private result;
44
+ }