@xoxno/sdk-js 1.0.223 → 1.0.225
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/dist/cjs/sdk/stellar/__tests__/admin.test.d.ts +0 -12
- package/dist/cjs/sdk/stellar/__tests__/builders-hubasset.test.d.ts +0 -11
- package/dist/cjs/sdk/stellar/__tests__/governance.test.d.ts +0 -4
- package/dist/cjs/sdk/stellar/__tests__/lending.test.d.ts +0 -20
- package/dist/cjs/sdk/stellar/__tests__/strategy-program.test.d.ts +0 -8
- package/dist/cjs/sdk/stellar/__tests__/swap-history.test.d.ts +1 -0
- package/dist/cjs/sdk/stellar/index.d.ts +1 -0
- package/dist/cjs/sdk/stellar/swap-history.d.ts +25 -0
- package/dist/cjs/sdk/stellar/swap-index.d.ts +2 -0
- package/dist/cjs/sdk/stellar/xdr-compat.d.ts +11 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/sdk/index.js +198 -0
- package/dist/esm/sdk/stellar/admin.js +385 -0
- package/dist/esm/sdk/stellar/auth-policy.js +310 -0
- package/dist/esm/sdk/stellar/cctp.js +23 -0
- package/dist/esm/sdk/stellar/contracts.js +49 -0
- package/dist/esm/sdk/stellar/events/decode.js +415 -0
- package/dist/esm/sdk/stellar/events/id.js +62 -0
- package/dist/esm/sdk/stellar/events/index.js +2 -0
- package/dist/esm/sdk/stellar/governance.js +403 -0
- package/dist/esm/sdk/stellar/index.js +25 -0
- package/dist/esm/sdk/stellar/lending-api-types.js +3 -0
- package/dist/esm/sdk/stellar/lending-read-types.js +1 -0
- package/dist/esm/sdk/stellar/lending-read.js +341 -0
- package/dist/esm/sdk/stellar/lending.js +358 -0
- package/dist/esm/sdk/stellar/math/account.js +256 -0
- package/dist/esm/sdk/stellar/math/index.js +11 -0
- package/dist/esm/sdk/stellar/math/limits.js +273 -0
- package/dist/esm/sdk/stellar/math/projection.js +130 -0
- package/dist/esm/sdk/stellar/math/rates.js +62 -0
- package/dist/esm/sdk/stellar/math/scaled.js +156 -0
- package/dist/esm/sdk/stellar/position-mode.js +11 -0
- package/dist/esm/sdk/stellar/prepare.js +47 -0
- package/dist/esm/sdk/stellar/quote.js +87 -0
- package/dist/esm/sdk/stellar/repay-swap.js +12 -0
- package/dist/esm/sdk/stellar/route-verify.js +285 -0
- package/dist/esm/sdk/stellar/scval-encode.js +464 -0
- package/dist/esm/sdk/stellar/swap-history.js +46 -0
- package/dist/esm/sdk/stellar/swap-index.js +1 -0
- package/dist/esm/sdk/stellar/swap.js +117 -0
- package/dist/esm/sdk/stellar/xdr-compat.js +17 -0
- package/dist/esm/sdk/stellar-schema.js +129 -0
- package/dist/esm/sdk/swagger.js +1957 -0
- package/dist/esm/sdk/types.js +1 -0
- package/dist/esm/sdk/utils.js +21 -0
- package/dist/esm/utils/api.js +169 -0
- package/dist/esm/utils/const.js +11 -0
- package/dist/esm/utils/errors.js +20 -0
- package/dist/esm/utils/guards.js +29 -0
- package/dist/esm/utils/helpers.js +20 -0
- package/dist/esm/utils/regex.js +14 -0
- package/dist/esm/utils/soroban-errors.js +149 -0
- package/dist/index.cjs +1 -1
- package/dist/index.esm.js +1 -2
- package/dist/sdk/stellar/__tests__/admin.test.d.ts +0 -12
- package/dist/sdk/stellar/__tests__/builders-hubasset.test.d.ts +0 -11
- package/dist/sdk/stellar/__tests__/governance.test.d.ts +0 -4
- package/dist/sdk/stellar/__tests__/lending.test.d.ts +0 -20
- package/dist/sdk/stellar/__tests__/strategy-program.test.d.ts +0 -8
- package/dist/sdk/stellar/__tests__/swap-history.test.d.ts +1 -0
- package/dist/sdk/stellar/index.cjs +1 -1
- package/dist/sdk/stellar/index.d.ts +1 -0
- package/dist/sdk/stellar/index.esm.js +1 -1
- package/dist/sdk/stellar/swap-history.d.ts +25 -0
- package/dist/sdk/stellar/swap-index.cjs +2 -0
- package/dist/sdk/stellar/swap-index.d.ts +2 -0
- package/dist/sdk/stellar/swap-index.esm.js +2 -0
- package/dist/sdk/stellar/swap-index.esm.js.LICENSE.txt +8 -0
- package/dist/sdk/stellar/xdr-compat.d.ts +11 -0
- package/package.json +16 -5
- /package/dist/{index.esm.js.LICENSE.txt → sdk/stellar/swap-index.cjs.LICENSE.txt} +0 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Pre-signing allowlist for the Soroban authorization tree of a PREPARED
|
|
16
|
+
* transaction.
|
|
17
|
+
*
|
|
18
|
+
* Simulation records every `require_auth` that code on the call stack makes
|
|
19
|
+
* for the caller — including one made by a rogue pool or token inside a swap
|
|
20
|
+
* hop, e.g. `any_token.transfer(caller, attacker, x)`. The wallet signature
|
|
21
|
+
* covers the whole tree, so whatever `prepareTransaction` returns is what the
|
|
22
|
+
* user authorizes. Each policy below is the set of sub-invocations the
|
|
23
|
+
* contracts legitimately request from the caller, with the source that proves
|
|
24
|
+
* it (paths are in the `rs-lending-xlm` repository):
|
|
25
|
+
*
|
|
26
|
+
* | Entrypoint | Caller sub-invocations | Source |
|
|
27
|
+
* |---|---|---|
|
|
28
|
+
* | controller `supply` | `asset.transfer(caller, pool, <= amount)` per asset | `contracts/controller/src/positions/supply.rs:118` |
|
|
29
|
+
* | controller `repay` | `asset.transfer(caller, pool, <= amount)` per asset | `contracts/controller/src/positions/debt.rs:144` |
|
|
30
|
+
* | controller `liquidate` | `asset.transfer(liquidator, pool, <= payment)` per asset | `contracts/controller/src/positions/liquidation/apply.rs:50` |
|
|
31
|
+
* | controller `multiply` | `asset.transfer(caller, controller, <= initial_payment)` when present | `contracts/controller/src/strategies/multiply.rs:169` |
|
|
32
|
+
* | controller `migrate_from_blend` | `blend_pool.submit(caller, controller, controller, [withdraw / withdraw_collateral / repay])`, at most twice | `contracts/controller/src/external/blend.rs:46-97` |
|
|
33
|
+
* | controller `borrow`, `withdraw`, `swap_debt`, `swap_collateral`, `repay_debt_with_collateral`, `flash_loan`, `flash_position`, any other | none | the controller funds these from its own custody (`strategies/legs.rs:50`, `strategies/flash_position.rs:285`) |
|
|
34
|
+
* | governance, any | none | proposer auth only |
|
|
35
|
+
* | router `execute_strategy` | `token_in.transfer(sender, router, <= total_in)` | `contracts/swap-aggregator/src/execute/mod.rs:81` |
|
|
36
|
+
*/ import { Address, scValToBigInt, StrKey, xdr } from "@stellar/stellar-sdk";
|
|
37
|
+
import { getStellarDeployment } from "./contracts.js";
|
|
38
|
+
import { decodeStellarRouteBytes } from "./route-verify.js";
|
|
39
|
+
import { xdrBytes, xdrField, xdrOpaque, xdrType } from "./xdr-compat.js";
|
|
40
|
+
/** Thrown when the authorization tree holds anything the policy does not allow. */ export class StellarAuthPolicyError extends Error {
|
|
41
|
+
constructor(code, message, offender){
|
|
42
|
+
const where = offender ? ` — ${offender.contract}.${offender.fn}(${offender.args.join(', ')})` : '';
|
|
43
|
+
super(`Stellar authorization check failed [${code}]: ${message}${where}`), _define_property(this, "code", void 0), _define_property(this, "offender", void 0);
|
|
44
|
+
this.name = 'StellarAuthPolicyError';
|
|
45
|
+
this.code = code;
|
|
46
|
+
this.offender = offender;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const addressOf = (value)=>value && xdrType(value) === 'scvAddress' ? Address.fromScAddress(xdrField(value, 'address')).toString() : undefined;
|
|
50
|
+
const i128Of = (value)=>value && xdrType(value) === 'scvI128' ? scValToBigInt(value) : undefined;
|
|
51
|
+
const vecOf = (value)=>value && xdrType(value) === 'scvVec' ? xdrField(value, 'vec') ?? [] : undefined;
|
|
52
|
+
const fieldOf = (value, name)=>{
|
|
53
|
+
if (!value || xdrType(value) !== 'scvMap') return undefined;
|
|
54
|
+
const entry = (xdrField(value, 'map') ?? []).find((e)=>xdrType(xdrField(e, 'key')) === 'scvSymbol' && xdrField(xdrField(e, 'key'), 'sym').toString() === name);
|
|
55
|
+
return entry ? xdrField(entry, 'val') : undefined;
|
|
56
|
+
};
|
|
57
|
+
const describeArg = (value)=>addressOf(value) ?? i128Of(value)?.toString() ?? value.toXDR('base64');
|
|
58
|
+
const describe = (args)=>({
|
|
59
|
+
contract: Address.fromScAddress(xdrField(args, 'contractAddress')).toString(),
|
|
60
|
+
fn: xdrField(args, 'functionName').toString(),
|
|
61
|
+
args: xdrField(args, 'args').map(describeArg)
|
|
62
|
+
});
|
|
63
|
+
const contractFnOf = (invocation)=>{
|
|
64
|
+
const fn = xdrField(invocation, 'function');
|
|
65
|
+
return xdrType(fn) === 'sorobanAuthorizedFunctionTypeContractFn' ? xdrField(fn, 'contractFn') : undefined;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Enforce `policy` on the authorization entries of a prepared transaction.
|
|
69
|
+
* Entries that belong to other addresses are ignored. For the caller: at most
|
|
70
|
+
* one entry, the expected root, and only allowlisted leaf sub-invocations.
|
|
71
|
+
* @param sourceAccount - Account that source-account credentials stand for
|
|
72
|
+
* (the operation source, else the transaction source). When omitted, such
|
|
73
|
+
* entries are treated as the caller's.
|
|
74
|
+
* @throws StellarAuthPolicyError
|
|
75
|
+
*/ export function assertStellarAuthEntries(entries, policy, sourceAccount) {
|
|
76
|
+
const mine = entries.filter((entry)=>{
|
|
77
|
+
const credentials = xdrField(entry, 'credentials');
|
|
78
|
+
const kind = xdrType(credentials);
|
|
79
|
+
if (kind !== 'sorobanCredentialsAddress' && kind !== 'sorobanCredentialsSourceAccount') {
|
|
80
|
+
throw new StellarAuthPolicyError('MALFORMED_TRANSACTION', 'unsupported authorization credentials');
|
|
81
|
+
}
|
|
82
|
+
const signer = kind === 'sorobanCredentialsAddress' ? Address.fromScAddress(xdrField(xdrField(credentials, 'address'), 'address')).toString() : sourceAccount ?? policy.caller;
|
|
83
|
+
return signer === policy.caller;
|
|
84
|
+
});
|
|
85
|
+
if (mine.length > 1) {
|
|
86
|
+
throw new StellarAuthPolicyError('DUPLICATE_ENTRY', `${mine.length} authorization entries for ${policy.caller}, expected one`);
|
|
87
|
+
}
|
|
88
|
+
const entry = mine[0];
|
|
89
|
+
if (!entry) return;
|
|
90
|
+
const root = contractFnOf(xdrField(entry, 'rootInvocation'));
|
|
91
|
+
if (!root) {
|
|
92
|
+
throw new StellarAuthPolicyError('UNEXPECTED_ROOT', 'root is not a contract call');
|
|
93
|
+
}
|
|
94
|
+
const rootInfo = describe(root);
|
|
95
|
+
if (rootInfo.contract !== policy.root.contract || rootInfo.fn !== policy.root.fn) {
|
|
96
|
+
throw new StellarAuthPolicyError('UNEXPECTED_ROOT', `expected ${policy.root.contract}.${policy.root.fn}`, rootInfo);
|
|
97
|
+
}
|
|
98
|
+
const spent = policy.transfers.map(()=>0n);
|
|
99
|
+
const called = (policy.calls ?? []).map(()=>0);
|
|
100
|
+
for (const child of xdrField(xdrField(entry, 'rootInvocation'), 'subInvocations')){
|
|
101
|
+
const call = contractFnOf(child);
|
|
102
|
+
if (!call) {
|
|
103
|
+
throw new StellarAuthPolicyError('UNEXPECTED_CALL', 'sub-invocation is not a contract call');
|
|
104
|
+
}
|
|
105
|
+
const info = describe(call);
|
|
106
|
+
if (xdrField(child, 'subInvocations').length > 0) {
|
|
107
|
+
throw new StellarAuthPolicyError('NESTED_INVOCATION', 'sub-invocation carries its own sub-invocations', info);
|
|
108
|
+
}
|
|
109
|
+
const allowedCall = (policy.calls ?? []).findIndex((c)=>c.contract === info.contract && c.fn === info.fn);
|
|
110
|
+
if (allowedCall !== -1) {
|
|
111
|
+
const allowance = (policy.calls ?? [])[allowedCall];
|
|
112
|
+
const reason = allowance.reject(xdrField(call, 'args'));
|
|
113
|
+
if (reason) throw new StellarAuthPolicyError('UNEXPECTED_CALL', reason, info);
|
|
114
|
+
called[allowedCall] = (called[allowedCall] ?? 0) + 1;
|
|
115
|
+
if ((called[allowedCall] ?? 0) > allowance.maxCount) {
|
|
116
|
+
throw new StellarAuthPolicyError('UNEXPECTED_CALL', `more than ${allowance.maxCount} calls`, info);
|
|
117
|
+
}
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (info.fn !== 'transfer' || xdrField(call, 'args').length !== 3) {
|
|
121
|
+
throw new StellarAuthPolicyError('UNEXPECTED_CALL', 'only `transfer` is allowed', info);
|
|
122
|
+
}
|
|
123
|
+
if (!policy.transfers.some((t)=>t.token === info.contract)) {
|
|
124
|
+
throw new StellarAuthPolicyError('UNEXPECTED_TOKEN', 'transfer on an unexpected token', info);
|
|
125
|
+
}
|
|
126
|
+
const [from, to, amount] = xdrField(call, 'args');
|
|
127
|
+
if (addressOf(from) !== policy.caller) {
|
|
128
|
+
throw new StellarAuthPolicyError('UNEXPECTED_SENDER', 'transfer is not from the caller', info);
|
|
129
|
+
}
|
|
130
|
+
const recipient = addressOf(to);
|
|
131
|
+
const slot = policy.transfers.findIndex((t)=>t.token === info.contract && t.to === recipient);
|
|
132
|
+
if (slot === -1) {
|
|
133
|
+
throw new StellarAuthPolicyError('UNEXPECTED_RECIPIENT', 'transfer to an unexpected recipient', info);
|
|
134
|
+
}
|
|
135
|
+
const value = i128Of(amount);
|
|
136
|
+
const allowance = policy.transfers[slot];
|
|
137
|
+
const total = (spent[slot] ?? 0n) + (value ?? 0n);
|
|
138
|
+
if (value === undefined || value <= 0n || total > allowance.maxAmount) {
|
|
139
|
+
throw new StellarAuthPolicyError('AMOUNT_EXCEEDED', `transfers total ${total}, above the expected ${allowance.maxAmount}`, info);
|
|
140
|
+
}
|
|
141
|
+
spent[slot] = total;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const malformed = (message)=>{
|
|
145
|
+
throw new StellarAuthPolicyError('MALFORMED_TRANSACTION', message);
|
|
146
|
+
};
|
|
147
|
+
/** Sum `Vec<(HubAssetKey, i128)>` per token into transfer allowances to `to`. */ const hubPaymentAllowances = (value, to)=>{
|
|
148
|
+
const totals = new Map();
|
|
149
|
+
for (const tuple of vecOf(value) ?? malformed('payments argument is not a vector')){
|
|
150
|
+
const [key, amount] = vecOf(tuple) ?? [];
|
|
151
|
+
const token = addressOf(fieldOf(key, 'asset'));
|
|
152
|
+
const parsed = i128Of(amount);
|
|
153
|
+
if (token === undefined || parsed === undefined || parsed < 0n) {
|
|
154
|
+
return malformed('payment is not a (HubAssetKey, i128) tuple');
|
|
155
|
+
}
|
|
156
|
+
totals.set(token, (totals.get(token) ?? 0n) + parsed);
|
|
157
|
+
}
|
|
158
|
+
return [
|
|
159
|
+
...totals
|
|
160
|
+
].map(([token, maxAmount])=>({
|
|
161
|
+
token,
|
|
162
|
+
to,
|
|
163
|
+
maxAmount
|
|
164
|
+
}));
|
|
165
|
+
};
|
|
166
|
+
// Blend `submit` request types the controller sends (external/blend.rs:7-9).
|
|
167
|
+
const BLEND_MIGRATION_REQUEST_TYPES = new Set([
|
|
168
|
+
1,
|
|
169
|
+
3,
|
|
170
|
+
5
|
|
171
|
+
]);
|
|
172
|
+
const blendSubmitAllowance = (blendPool, caller, controller)=>({
|
|
173
|
+
contract: blendPool,
|
|
174
|
+
fn: 'submit',
|
|
175
|
+
// One repay submit and one withdraw-all submit.
|
|
176
|
+
maxCount: 2,
|
|
177
|
+
reject: (args)=>{
|
|
178
|
+
if (args.length !== 4) return 'submit must have four arguments';
|
|
179
|
+
if (addressOf(args[0]) !== caller) return '`from` is not the caller';
|
|
180
|
+
if (addressOf(args[1]) !== controller || addressOf(args[2]) !== controller) {
|
|
181
|
+
return '`spender` and `to` must be the lending controller';
|
|
182
|
+
}
|
|
183
|
+
const requests = vecOf(args[3]);
|
|
184
|
+
if (!requests) return 'requests is not a vector';
|
|
185
|
+
for (const request of requests){
|
|
186
|
+
const type = fieldOf(request, 'request_type');
|
|
187
|
+
if (!type || xdrType(type) !== 'scvU32' || !BLEND_MIGRATION_REQUEST_TYPES.has(xdrField(type, 'u32'))) {
|
|
188
|
+
return 'request is not a withdraw, withdraw-collateral, or repay';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
/**
|
|
195
|
+
* Derive the policy for one contract invocation from its own arguments. The
|
|
196
|
+
* arguments are what the application built from the user's intent, so the
|
|
197
|
+
* allowed transfers are bounded by exactly what the user asked to pay.
|
|
198
|
+
* @throws StellarAuthPolicyError for a contract outside `deployment`.
|
|
199
|
+
*/ export function stellarAuthPolicyForInvocation(caller, invocation, deployment) {
|
|
200
|
+
const { contract, fn } = describe(invocation);
|
|
201
|
+
const args = xdrField(invocation, 'args');
|
|
202
|
+
const policy = {
|
|
203
|
+
caller,
|
|
204
|
+
root: {
|
|
205
|
+
contract,
|
|
206
|
+
fn
|
|
207
|
+
},
|
|
208
|
+
transfers: []
|
|
209
|
+
};
|
|
210
|
+
if (contract === deployment.governance) return policy;
|
|
211
|
+
if (contract === deployment.aggregatorRouter) {
|
|
212
|
+
if (fn !== 'execute_strategy') return policy;
|
|
213
|
+
const totalIn = i128Of(args[1]);
|
|
214
|
+
if (addressOf(args[0]) !== caller || totalIn === undefined || xdrType(args[2]) !== 'scvBytes') {
|
|
215
|
+
return malformed('execute_strategy arguments must be (caller, total_in, swap_xdr)');
|
|
216
|
+
}
|
|
217
|
+
const route = decodeStellarRouteBytes(xdrBytes(args[2]));
|
|
218
|
+
policy.transfers = [
|
|
219
|
+
{
|
|
220
|
+
token: route.tokenIn,
|
|
221
|
+
to: contract,
|
|
222
|
+
maxAmount: totalIn
|
|
223
|
+
}
|
|
224
|
+
];
|
|
225
|
+
return policy;
|
|
226
|
+
}
|
|
227
|
+
if (contract !== deployment.lendingController) {
|
|
228
|
+
throw new StellarAuthPolicyError('UNSUPPORTED_CONTRACT', `no authorization policy for contract ${contract}; pass an explicit policy`);
|
|
229
|
+
}
|
|
230
|
+
if (addressOf(args[0]) !== caller) {
|
|
231
|
+
throw new StellarAuthPolicyError('CALLER_MISMATCH', `${fn} is not invoked for ${caller}`);
|
|
232
|
+
}
|
|
233
|
+
switch(fn){
|
|
234
|
+
case 'supply':
|
|
235
|
+
policy.transfers = hubPaymentAllowances(args[3], deployment.lendingPool);
|
|
236
|
+
break;
|
|
237
|
+
case 'repay':
|
|
238
|
+
case 'liquidate':
|
|
239
|
+
policy.transfers = hubPaymentAllowances(args[2], deployment.lendingPool);
|
|
240
|
+
break;
|
|
241
|
+
case 'multiply':
|
|
242
|
+
{
|
|
243
|
+
// initial_payment: Option<(HubAssetKey, i128)> pays the controller.
|
|
244
|
+
const payment = args[8];
|
|
245
|
+
if (payment && xdrType(payment) !== 'scvVoid') {
|
|
246
|
+
policy.transfers = hubPaymentAllowances(xdr.ScVal.scvVec([
|
|
247
|
+
payment
|
|
248
|
+
]), contract);
|
|
249
|
+
}
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
case 'migrate_from_blend':
|
|
253
|
+
{
|
|
254
|
+
const blendPool = addressOf(args[4]) ?? malformed('blend_pool is not an address');
|
|
255
|
+
policy.calls = [
|
|
256
|
+
blendSubmitAllowance(blendPool, caller, contract)
|
|
257
|
+
];
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
default:
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
return policy;
|
|
264
|
+
}
|
|
265
|
+
const accountOf = (muxed)=>StrKey.encodeEd25519PublicKey(xdrOpaque(xdrType(muxed) === 'keyTypeMuxedEd25519' ? xdrField(xdrField(muxed, 'med25519'), 'ed25519') : xdrField(muxed, 'ed25519')));
|
|
266
|
+
const singleInvocation = (envelopeXdr)=>{
|
|
267
|
+
let envelope;
|
|
268
|
+
try {
|
|
269
|
+
envelope = xdr.TransactionEnvelope.fromXDR(envelopeXdr, 'base64');
|
|
270
|
+
} catch {
|
|
271
|
+
return malformed('not a transaction envelope');
|
|
272
|
+
}
|
|
273
|
+
if (xdrType(envelope) !== 'envelopeTypeTx') {
|
|
274
|
+
return malformed('expected a regular (v1) transaction');
|
|
275
|
+
}
|
|
276
|
+
const tx = xdrField(xdrField(envelope, 'v1'), 'tx');
|
|
277
|
+
const operation = xdrField(tx, 'operations')[0];
|
|
278
|
+
if (!operation || xdrField(tx, 'operations').length !== 1) {
|
|
279
|
+
return malformed('expected exactly one operation');
|
|
280
|
+
}
|
|
281
|
+
const body = xdrField(operation, 'body');
|
|
282
|
+
if (xdrType(body) !== 'invokeHostFunction') {
|
|
283
|
+
return malformed('operation is not a contract invocation');
|
|
284
|
+
}
|
|
285
|
+
const op = xdrField(body, 'invokeHostFunctionOp');
|
|
286
|
+
if (xdrType(xdrField(op, 'hostFunction')) !== 'hostFunctionTypeInvokeContract') {
|
|
287
|
+
return malformed('host function is not a contract call');
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
source: accountOf(xdrField(operation, 'sourceAccount') ?? xdrField(tx, 'sourceAccount')),
|
|
291
|
+
op,
|
|
292
|
+
call: xdrField(xdrField(op, 'hostFunction'), 'invokeContract')
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* Check a prepared transaction (base64 XDR, as `prepareTransaction` returns
|
|
297
|
+
* it) before it is passed to a wallet. Takes XDR strings so no live SDK object
|
|
298
|
+
* crosses the consumer's bundle boundary. Never sign when this throws.
|
|
299
|
+
* @throws StellarAuthPolicyError | StellarRouteVerificationError
|
|
300
|
+
*/ export function assertStellarPreparedTxAuth(preparedXdr, opts) {
|
|
301
|
+
const prepared = singleInvocation(preparedXdr);
|
|
302
|
+
if (opts.builtXdr !== undefined && xdrField(singleInvocation(opts.builtXdr).op, 'hostFunction').toXDR('base64') !== xdrField(prepared.op, 'hostFunction').toXDR('base64')) {
|
|
303
|
+
throw new StellarAuthPolicyError('INVOCATION_CHANGED', 'the prepared transaction does not invoke what was built', describe(prepared.call));
|
|
304
|
+
}
|
|
305
|
+
const policy = opts.policy ?? stellarAuthPolicyForInvocation(opts.caller, prepared.call, {
|
|
306
|
+
...getStellarDeployment(opts.network),
|
|
307
|
+
...opts.deployment
|
|
308
|
+
});
|
|
309
|
+
assertStellarAuthEntries(xdrField(prepared.op, 'auth'), policy, prepared.source);
|
|
310
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Account, Contract, TransactionBuilder, xdr } from "@stellar/stellar-sdk";
|
|
2
|
+
import { Buffer } from "buffer";
|
|
3
|
+
import { STELLAR_NETWORK_PASSPHRASE } from "./contracts.js";
|
|
4
|
+
const hexToBuffer = (hex)=>{
|
|
5
|
+
const normalized = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
6
|
+
if (normalized.length % 2 !== 0) {
|
|
7
|
+
throw new Error('buildStellarCctpForwardTx: hex input must have even length');
|
|
8
|
+
}
|
|
9
|
+
return Buffer.from(normalized, 'hex');
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Build an unsigned XDR for the Stellar CCTP forwarder `mint_and_forward`.
|
|
13
|
+
*/ export function buildStellarCctpForwardTx(opts) {
|
|
14
|
+
const source = new Account(opts.caller, opts.sourceSequence);
|
|
15
|
+
const operation = new Contract(opts.forwarderAddress).call('mint_and_forward', xdr.ScVal.scvBytes(hexToBuffer(opts.message)), xdr.ScVal.scvBytes(hexToBuffer(opts.attestation)));
|
|
16
|
+
const tx = new TransactionBuilder(source, {
|
|
17
|
+
fee: opts.fee ?? '10000000',
|
|
18
|
+
networkPassphrase: STELLAR_NETWORK_PASSPHRASE[opts.network]
|
|
19
|
+
}).addOperation(operation).setTimeout(opts.timeoutSeconds ?? 120).build();
|
|
20
|
+
return {
|
|
21
|
+
xdr: tx.toXDR()
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Stellar network identifiers understood by the Soroban transaction builders. */ /**
|
|
2
|
+
* XOXNO's supported Stellar deployments. Keep this table in the SDK so UI
|
|
3
|
+
* and integrators use the same network, RPC, quote-server, and contract IDs.
|
|
4
|
+
* It contains no environment or runtime discovery logic.
|
|
5
|
+
*/ export const STELLAR_NETWORKS = {
|
|
6
|
+
stellarMainnet: {
|
|
7
|
+
name: 'mainnet',
|
|
8
|
+
passphrase: 'Public Global Stellar Network ; September 2015',
|
|
9
|
+
horizonUrl: 'https://horizon.stellar.org',
|
|
10
|
+
sorobanRpcUrl: 'https://stellar-gateway.xoxno.com',
|
|
11
|
+
quoteUrl: 'https://stellar-swap.xoxno.com',
|
|
12
|
+
explorerNetwork: 'public',
|
|
13
|
+
aquariusApiUrl: 'https://amm-api.aqua.network',
|
|
14
|
+
aquariusRouter: 'CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK',
|
|
15
|
+
lendingController: 'CAUCMIN5KSXEVZ7NMXR3LZATGD5EFIEUI5XWTFLYRO2R5OTXI22WE5JX',
|
|
16
|
+
lendingPool: 'CBXRNDQMAJFG4VUKMKFEMFS75UUXE2SPSNV4LEEFCSNBCN66PYRWBKXO',
|
|
17
|
+
aggregatorRouter: 'CCVENFSVCBYDHVOACFZXMNNYVOZ3LKXPZYU5LUI4N7KTXOKRVYD7F3TR',
|
|
18
|
+
governance: 'CC44PEQW7HSEPKAZ5ZRPH2JS5M5KVJXCUBLJ2ZX4E3WCDKMNFILHC2AD',
|
|
19
|
+
priceAggregator: 'CBGUF2G2Q7HCVCWYISDXBHPVBGMYNXA7PG2VET66YBZX6IKOOV27NSMV',
|
|
20
|
+
positionNftContract: 'CAWCSG77AY2W24QZ6ZXLHZU4UXEFHZBM6EI4A4IF7JB5CTY4XND3TI6C'
|
|
21
|
+
},
|
|
22
|
+
stellarTestnet: {
|
|
23
|
+
name: 'testnet',
|
|
24
|
+
passphrase: 'Test SDF Network ; September 2015',
|
|
25
|
+
horizonUrl: 'https://horizon-testnet.stellar.org',
|
|
26
|
+
sorobanRpcUrl: 'https://stellar-testnet-gateway.xoxno.com',
|
|
27
|
+
quoteUrl: 'https://testnet-stellar-swap.xoxno.com',
|
|
28
|
+
explorerNetwork: 'testnet',
|
|
29
|
+
aquariusApiUrl: 'https://amm-api-testnet.aqua.network',
|
|
30
|
+
aquariusRouter: 'CBCFTQSPDBAIZ6R6PJQKSQWKNKWH2QIV3I4J72SHWBIK3ADRRAM5A6GD',
|
|
31
|
+
lendingController: 'CCXRWJ6SIU2WPFEGLFGJVITPL57QAYIMIO6OAM2NBGNDQSSCK2FFV3F3',
|
|
32
|
+
lendingPool: 'CBSGF6QOQAMPFBEVSYPEQHSZRIHJ6RCGUPCRDMUX36DEKRWFAO2PZB5A',
|
|
33
|
+
aggregatorRouter: 'CDNTWMWW2WGYTKIZTJYNGNVQQZI4KTC5BQRZ3275KESRX5T4O3AYECL5',
|
|
34
|
+
governance: 'CDS33JDOYH3F3FL4QUQ6DV4WKHML2AKHF4LADTZ57FRUAEBTE7NMY5FQ',
|
|
35
|
+
priceAggregator: 'CAALOOTIDXCX7D7FMQIBSSAJLPKOM3GMXS4UUSTDIG42JCRRJHOPUHOP',
|
|
36
|
+
positionNftContract: 'CDVN5JU675MEDPVRPCYC45AHFC275UH57WEU5OTFE4WFGZBNN7HTLPSY'
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
/** Lowercase alias for consumers that prefer the UI's existing naming. */ export const stellarNetworks = STELLAR_NETWORKS;
|
|
40
|
+
/** Resolve a deployment from the network name used by transaction builders. */ export function getStellarDeployment(network) {
|
|
41
|
+
return STELLAR_NETWORKS[network === 'testnet' ? 'stellarTestnet' : 'stellarMainnet'];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Stellar network passphrases (Soroban tx signing domain separator).
|
|
45
|
+
* These are fixed by the Stellar network itself and must not be overridden.
|
|
46
|
+
*/ export const STELLAR_NETWORK_PASSPHRASE = {
|
|
47
|
+
mainnet: STELLAR_NETWORKS.stellarMainnet.passphrase,
|
|
48
|
+
testnet: STELLAR_NETWORKS.stellarTestnet.passphrase
|
|
49
|
+
};
|