@keetanetwork/anchor 0.0.69 → 0.0.71
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/lib/chaining.d.ts +26 -6
- package/lib/chaining.d.ts.map +1 -1
- package/lib/chaining.js +521 -113
- package/lib/chaining.js.map +1 -1
- package/lib/resolver.js +33 -33
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/services/asset-movement/client.d.ts.map +1 -1
- package/services/asset-movement/client.js +5 -1
- package/services/asset-movement/client.js.map +1 -1
- package/services/asset-movement/common.d.ts +1 -1
- package/services/asset-movement/common.d.ts.map +1 -1
- package/services/asset-movement/common.generated.js +143922 -35812
- package/services/asset-movement/common.generated.js.map +1 -1
- package/services/asset-movement/common.js.map +1 -1
- package/services/asset-movement/lib/data/addresses/bank-account/ar.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ar.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ar.js +42 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ar.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/au.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/au.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/au.js +45 -0
- package/services/asset-movement/lib/data/addresses/bank-account/au.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bd.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bd.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bd.js +42 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bd.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bf.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bf.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bf.js +27 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bf.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bj.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bj.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bj.js +27 -0
- package/services/asset-movement/lib/data/addresses/bank-account/bj.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ci.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ci.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ci.js +27 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ci.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ck.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ck.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ck.js +45 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ck.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cl.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cl.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cl.js +28 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cl.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cn.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cn.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cn.js +45 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cn.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/co.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/co.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/co.js +43 -0
- package/services/asset-movement/lib/data/addresses/bank-account/co.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cr.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cr.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cr.js +39 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cr.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cz.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cz.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cz.js +35 -0
- package/services/asset-movement/lib/data/addresses/bank-account/cz.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/dk.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/dk.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/dk.js +45 -0
- package/services/asset-movement/lib/data/addresses/bank-account/dk.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/eg.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/eg.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/eg.js +32 -0
- package/services/asset-movement/lib/data/addresses/bank-account/eg.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/fo.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/fo.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/fo.js +45 -0
- package/services/asset-movement/lib/data/addresses/bank-account/fo.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gg.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gg.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gg.js +32 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gg.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gh.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gh.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gh.js +31 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gh.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gi.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gi.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gi.js +32 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gi.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gl.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gl.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gl.js +45 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gl.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gm.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gm.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gm.js +28 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gm.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gn.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gn.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gn.js +35 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gn.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gt.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gt.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gt.js +32 -0
- package/services/asset-movement/lib/data/addresses/bank-account/gt.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/id.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/id.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/id.js +35 -0
- package/services/asset-movement/lib/data/addresses/bank-account/id.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/il.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/il.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/il.js +36 -0
- package/services/asset-movement/lib/data/addresses/bank-account/il.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/im.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/im.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/im.js +32 -0
- package/services/asset-movement/lib/data/addresses/bank-account/im.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/index.generated.d.ts +104 -0
- package/services/asset-movement/lib/data/addresses/bank-account/index.generated.d.ts.map +1 -1
- package/services/asset-movement/lib/data/addresses/bank-account/index.generated.js +105 -1
- package/services/asset-movement/lib/data/addresses/bank-account/index.generated.js.map +1 -1
- package/services/asset-movement/lib/data/addresses/bank-account/je.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/je.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/je.js +32 -0
- package/services/asset-movement/lib/data/addresses/bank-account/je.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jm.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jm.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jm.js +36 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jm.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jo.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jo.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jo.js +32 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jo.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jp.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jp.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jp.js +57 -0
- package/services/asset-movement/lib/data/addresses/bank-account/jp.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ke.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ke.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ke.js +38 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ke.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ki.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ki.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ki.js +45 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ki.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/kr.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/kr.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/kr.js +42 -0
- package/services/asset-movement/lib/data/addresses/bank-account/kr.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/lk.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/lk.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/lk.js +46 -0
- package/services/asset-movement/lib/data/addresses/bank-account/lk.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ma.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ma.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ma.js +32 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ma.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ng.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ng.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ng.js +39 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ng.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/np.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/np.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/np.js +49 -0
- package/services/asset-movement/lib/data/addresses/bank-account/np.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/nz.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/nz.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/nz.js +45 -0
- package/services/asset-movement/lib/data/addresses/bank-account/nz.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/pe.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/pe.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/pe.js +35 -0
- package/services/asset-movement/lib/data/addresses/bank-account/pe.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ph.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ph.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ph.js +52 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ph.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/pk.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/pk.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/pk.js +42 -0
- package/services/asset-movement/lib/data/addresses/bank-account/pk.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/sn.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/sn.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/sn.js +27 -0
- package/services/asset-movement/lib/data/addresses/bank-account/sn.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/sv.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/sv.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/sv.js +32 -0
- package/services/asset-movement/lib/data/addresses/bank-account/sv.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tg.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tg.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tg.js +27 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tg.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/th.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/th.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/th.js +35 -0
- package/services/asset-movement/lib/data/addresses/bank-account/th.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tn.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tn.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tn.js +36 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tn.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tr.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tr.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tr.js +28 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tr.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tv.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tv.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tv.js +45 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tv.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tz.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tz.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tz.js +35 -0
- package/services/asset-movement/lib/data/addresses/bank-account/tz.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ug.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ug.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ug.js +42 -0
- package/services/asset-movement/lib/data/addresses/bank-account/ug.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/vn.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/vn.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/vn.js +42 -0
- package/services/asset-movement/lib/data/addresses/bank-account/vn.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/za.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/za.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/za.js +44 -0
- package/services/asset-movement/lib/data/addresses/bank-account/za.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/zm.d.ts +4 -0
- package/services/asset-movement/lib/data/addresses/bank-account/zm.d.ts.map +1 -0
- package/services/asset-movement/lib/data/addresses/bank-account/zm.js +28 -0
- package/services/asset-movement/lib/data/addresses/bank-account/zm.js.map +1 -0
- package/services/asset-movement/lib/data/addresses/types.generated.d.ts +6584 -794
- package/services/asset-movement/lib/data/addresses/types.generated.d.ts.map +1 -1
- package/services/asset-movement/lib/data/addresses/types.generated.js +6169 -478
- package/services/asset-movement/lib/data/addresses/types.generated.js.map +1 -1
- package/services/asset-movement/lib/location.generated.js +3 -3
- package/services/fx/common.js +4 -4
- package/services/kyc/common.generated.js +2 -2
- package/services/notification/common.generated.js +3 -3
- package/services/storage/clients/contacts.generated.js +8887 -1093
- package/services/storage/clients/contacts.generated.js.map +1 -1
- package/services/username/common.generated.js +9 -9
package/lib/chaining.js
CHANGED
|
@@ -11,6 +11,7 @@ import { isExternalChainAsset } from './asset.js';
|
|
|
11
11
|
;
|
|
12
12
|
;
|
|
13
13
|
;
|
|
14
|
+
;
|
|
14
15
|
function areBothTokenAndEqual(a, b) {
|
|
15
16
|
try {
|
|
16
17
|
const aParsed = KeetaNet.lib.Account.toAccount(a);
|
|
@@ -129,6 +130,11 @@ class AnchorGraph {
|
|
|
129
130
|
if (!fromEntries) {
|
|
130
131
|
return (null);
|
|
131
132
|
}
|
|
133
|
+
const operations = await service.operations('object');
|
|
134
|
+
if (!operations.createExchange) {
|
|
135
|
+
this.logger?.debug('AnchorGraph::computeFXNodes', `FX service ${providerID} does not support createExchange operation, skipping`);
|
|
136
|
+
return (null);
|
|
137
|
+
}
|
|
132
138
|
const pathNodes = await Promise.all(fromEntries.map(async function (fromEntry) {
|
|
133
139
|
const pathNodesResult = [];
|
|
134
140
|
const parsedEntry = await fromEntry('object');
|
|
@@ -224,7 +230,33 @@ class AnchorGraph {
|
|
|
224
230
|
if (!isRail(railResolved)) {
|
|
225
231
|
throw (new Error(`Invalid rail format in extended details: ${railResolved}`));
|
|
226
232
|
}
|
|
227
|
-
|
|
233
|
+
let supportedOperations;
|
|
234
|
+
if ('supportedOperations' in extendedDetailsResolved && extendedDetailsResolved.supportedOperations) {
|
|
235
|
+
const opsResolved = await extendedDetailsResolved.supportedOperations('object');
|
|
236
|
+
if (opsResolved && typeof opsResolved === 'object' && !Array.isArray(opsResolved)) {
|
|
237
|
+
const parsed = {};
|
|
238
|
+
if ('createPersistentForwarding' in opsResolved && opsResolved.createPersistentForwarding) {
|
|
239
|
+
const val = await opsResolved.createPersistentForwarding('boolean');
|
|
240
|
+
if (typeof val === 'boolean') {
|
|
241
|
+
parsed.createPersistentForwarding = val;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if ('initiateTransfer' in opsResolved && opsResolved.initiateTransfer) {
|
|
245
|
+
const val = await opsResolved.initiateTransfer('boolean');
|
|
246
|
+
if (typeof val === 'boolean') {
|
|
247
|
+
parsed.initiateTransfer = val;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (Object.keys(parsed).length > 0) {
|
|
251
|
+
supportedOperations = parsed;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const result = { rail: railResolved };
|
|
256
|
+
if (supportedOperations) {
|
|
257
|
+
result.supportedOperations = supportedOperations;
|
|
258
|
+
}
|
|
259
|
+
return (result);
|
|
228
260
|
}
|
|
229
261
|
async #computeAssetMovementPairSide(pairSideInput) {
|
|
230
262
|
const pairSideResolved = await pairSideInput('object');
|
|
@@ -242,13 +274,13 @@ class AnchorGraph {
|
|
|
242
274
|
const railsResolved = await pairSideResolved.rails('object');
|
|
243
275
|
const rails = {
|
|
244
276
|
common: await Promise.all((await railsResolved.common?.('array'))?.map(async (commonInput) => {
|
|
245
|
-
return (
|
|
277
|
+
return (await this.#computeAssetRails(commonInput));
|
|
246
278
|
}) ?? []),
|
|
247
279
|
inbound: await Promise.all((await railsResolved.inbound?.('array'))?.map(async (commonInput) => {
|
|
248
|
-
return (
|
|
280
|
+
return (await this.#computeAssetRails(commonInput));
|
|
249
281
|
}) ?? []),
|
|
250
282
|
outbound: await Promise.all((await railsResolved.outbound?.('array'))?.map(async (commonInput) => {
|
|
251
|
-
return (
|
|
283
|
+
return (await this.#computeAssetRails(commonInput));
|
|
252
284
|
}) ?? [])
|
|
253
285
|
};
|
|
254
286
|
const id = await pairSideResolved.id('string');
|
|
@@ -267,6 +299,7 @@ class AnchorGraph {
|
|
|
267
299
|
return ([]);
|
|
268
300
|
}
|
|
269
301
|
const providerResults = await Promise.all(Object.entries(assetMovementServices).map(async ([providerID, service]) => {
|
|
302
|
+
const supportedOperationsMetadata = await service.operations('object');
|
|
270
303
|
const supportedAssetsEntries = await service.supportedAssets('array');
|
|
271
304
|
if (!supportedAssetsEntries) {
|
|
272
305
|
this.logger?.debug('AnchorGraph::computeAssetMovementNodes', `No supported assets found for provider ${providerID}`);
|
|
@@ -282,18 +315,49 @@ class AnchorGraph {
|
|
|
282
315
|
this.#computeAssetMovementPairSide(pairResolved[0]),
|
|
283
316
|
this.#computeAssetMovementPairSide(pairResolved[1])
|
|
284
317
|
]);
|
|
318
|
+
function getProviderSupportedOperationsForRail(railSpecific) {
|
|
319
|
+
const retval = {
|
|
320
|
+
createPersistentForwarding: supportedOperationsMetadata.createPersistentForwarding !== undefined,
|
|
321
|
+
initiateTransfer: supportedOperationsMetadata.initiateTransfer !== undefined
|
|
322
|
+
};
|
|
323
|
+
if (railSpecific) {
|
|
324
|
+
retval.createPersistentForwarding = railSpecific.createPersistentForwarding ?? false;
|
|
325
|
+
retval.initiateTransfer = railSpecific.initiateTransfer ?? false;
|
|
326
|
+
}
|
|
327
|
+
return (retval);
|
|
328
|
+
}
|
|
285
329
|
const pathNodes = [];
|
|
286
330
|
for (const [src, dest] of [
|
|
287
331
|
[fromResolved, toResolved],
|
|
288
332
|
[toResolved, fromResolved]
|
|
289
333
|
]) {
|
|
290
334
|
for (const inboundRail of [...(src.rails.common ?? []), ...(src.rails.inbound ?? [])]) {
|
|
335
|
+
/*
|
|
336
|
+
* Drop edges whose source rail explicitly cannot
|
|
337
|
+
* initiate a transfer and also cannot create a
|
|
338
|
+
* persistent forwarding address.
|
|
339
|
+
*/
|
|
340
|
+
const inboundSupportedOperations = getProviderSupportedOperationsForRail(inboundRail.supportedOperations);
|
|
341
|
+
if (inboundSupportedOperations.initiateTransfer === false && inboundSupportedOperations.createPersistentForwarding === false) {
|
|
342
|
+
this.logger?.debug('AnchorGraph::computeAssetMovementNodes', `Skipping ${providerID} edge from ${convertAssetLocationToString(src.location)} via rail ${inboundRail.rail}: neither initiateTransfer nor createPersistentForwarding supported`);
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
291
345
|
for (const outboundRail of [...(dest.rails.common ?? []), ...(dest.rails.outbound ?? [])]) {
|
|
292
346
|
pathNodes.push({
|
|
293
347
|
type: 'assetMovement',
|
|
294
348
|
providerID: providerID,
|
|
295
|
-
from: {
|
|
296
|
-
|
|
349
|
+
from: {
|
|
350
|
+
asset: src.id,
|
|
351
|
+
location: src.location,
|
|
352
|
+
rail: inboundRail.rail,
|
|
353
|
+
supportedOperations: getProviderSupportedOperationsForRail(inboundRail.supportedOperations)
|
|
354
|
+
},
|
|
355
|
+
to: {
|
|
356
|
+
asset: dest.id,
|
|
357
|
+
location: dest.location,
|
|
358
|
+
rail: outboundRail.rail,
|
|
359
|
+
supportedOperations: getProviderSupportedOperationsForRail(outboundRail.supportedOperations)
|
|
360
|
+
}
|
|
297
361
|
});
|
|
298
362
|
}
|
|
299
363
|
}
|
|
@@ -580,39 +644,35 @@ class AnchorGraph {
|
|
|
580
644
|
return (result.to);
|
|
581
645
|
}
|
|
582
646
|
}
|
|
583
|
-
async
|
|
584
|
-
if (!isExternalChainAsset(
|
|
585
|
-
return (
|
|
647
|
+
async getExternalAssetMetadata(asset, location, providerID) {
|
|
648
|
+
if (!isExternalChainAsset(asset)) {
|
|
649
|
+
return (undefined);
|
|
586
650
|
}
|
|
587
|
-
const providers = await this.getAssetMovementProvidersForAsset(
|
|
651
|
+
const providers = await this.getAssetMovementProvidersForAsset(asset, location);
|
|
588
652
|
if (!providers) {
|
|
589
|
-
return (
|
|
653
|
+
return (undefined);
|
|
590
654
|
}
|
|
591
|
-
if (
|
|
592
|
-
const found = providers[
|
|
655
|
+
if (providerID) {
|
|
656
|
+
const found = providers[providerID];
|
|
593
657
|
if (!found) {
|
|
594
|
-
return (
|
|
658
|
+
return (undefined);
|
|
595
659
|
}
|
|
596
|
-
|
|
597
|
-
if (!metadata) {
|
|
598
|
-
return (assetInfo);
|
|
599
|
-
}
|
|
600
|
-
return ({
|
|
601
|
-
...assetInfo,
|
|
602
|
-
metadata
|
|
603
|
-
});
|
|
660
|
+
return (found.provider.getAssetMetadataForLocation(location, asset) ?? undefined);
|
|
604
661
|
}
|
|
605
662
|
for (const { provider } of Object.values(providers)) {
|
|
606
|
-
const metadata = provider.getAssetMetadataForLocation(
|
|
607
|
-
if (
|
|
608
|
-
|
|
663
|
+
const metadata = provider.getAssetMetadataForLocation(location, asset);
|
|
664
|
+
if (metadata) {
|
|
665
|
+
return (metadata);
|
|
609
666
|
}
|
|
610
|
-
return ({
|
|
611
|
-
...assetInfo,
|
|
612
|
-
metadata
|
|
613
|
-
});
|
|
614
667
|
}
|
|
615
|
-
return (
|
|
668
|
+
return (undefined);
|
|
669
|
+
}
|
|
670
|
+
async #attachMetadata(assetInfo, options) {
|
|
671
|
+
const metadata = await this.getExternalAssetMetadata(assetInfo.asset, assetInfo.location, options?.providerID);
|
|
672
|
+
if (!metadata) {
|
|
673
|
+
return (assetInfo);
|
|
674
|
+
}
|
|
675
|
+
return ({ ...assetInfo, metadata });
|
|
616
676
|
}
|
|
617
677
|
async resolveAssetsWithMetadata(filter = {}, options) {
|
|
618
678
|
const result = await this.resolveAssets(filter);
|
|
@@ -773,13 +833,118 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
773
833
|
}
|
|
774
834
|
return (found);
|
|
775
835
|
};
|
|
836
|
+
const forwardedSteps = new Map();
|
|
837
|
+
for (let scanIndex = 0; scanIndex < this.path.length; scanIndex++) {
|
|
838
|
+
const scanStep = this.path[scanIndex];
|
|
839
|
+
if (!scanStep || scanStep.type !== 'assetMovement') {
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* PFR is selected in two cases:
|
|
844
|
+
* (a) the source rail explicitly cannot accept a managed transfer.
|
|
845
|
+
* (b) the prior step is also an asset-movement step (AMP -> AMP
|
|
846
|
+
* transition) and the source rail supports PFR.
|
|
847
|
+
*/
|
|
848
|
+
const priorStep = scanIndex > 0 ? this.path[scanIndex - 1] : null;
|
|
849
|
+
const isAmpToAmpTransition = priorStep?.type === 'assetMovement';
|
|
850
|
+
const pfrSupported = scanStep.from.supportedOperations?.createPersistentForwarding === true;
|
|
851
|
+
const initiateForbidden = scanStep.from.supportedOperations?.initiateTransfer === false;
|
|
852
|
+
const shouldUsePFR = initiateForbidden || (isAmpToAmpTransition && pfrSupported);
|
|
853
|
+
if (!shouldUsePFR) {
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
if (!pfrSupported) {
|
|
857
|
+
throw (new Error(`Asset movement provider ${scanStep.providerID} source rail ${scanStep.from.rail} at ${convertAssetLocationToString(scanStep.from.location)} declares initiateTransfer:false but does not support createPersistentForwarding`));
|
|
858
|
+
}
|
|
859
|
+
if (scanIndex !== this.path.length - 1) {
|
|
860
|
+
throw (new Error(`Persistent-forwarding (PersistentForwardingRelay-only) asset movement steps are currently only supported as the last step in a chain (step ${scanIndex} of ${this.path.length})`));
|
|
861
|
+
}
|
|
862
|
+
const destinationAddress = this.request.destination.recipient;
|
|
863
|
+
if (typeof destinationAddress !== 'string') {
|
|
864
|
+
throw (new Error(`Persistent-forwarding step at index ${scanIndex} requires the chain's destination recipient to be a resolved address string`));
|
|
865
|
+
}
|
|
866
|
+
const forwardedAssetPair = { from: scanStep.from.asset, to: scanStep.to.asset };
|
|
867
|
+
const forwardedProviders = await assetMovementClient.getProvidersForTransfer({ asset: forwardedAssetPair, from: scanStep.from.location, to: scanStep.to.location }, { providerIDs: [scanStep.providerID] });
|
|
868
|
+
if (!forwardedProviders?.[0] || forwardedProviders.length === 0) {
|
|
869
|
+
throw (new Error(`Could not get asset movement provider ${scanStep.providerID} for persistent-forwarding step at index ${scanIndex}`));
|
|
870
|
+
}
|
|
871
|
+
const forwardedProvider = forwardedProviders[0];
|
|
872
|
+
if (!await forwardedProvider.isOperationSupported('createPersistentForwarding')) {
|
|
873
|
+
throw (new Error(`Asset movement provider ${scanStep.providerID} does not support createPersistentForwarding, but the source rail ${scanStep.from.rail} at ${convertAssetLocationToString(scanStep.from.location)} requires it (initiateTransfer is unsupported)`));
|
|
874
|
+
}
|
|
875
|
+
if (!await forwardedProvider.isOperationSupported('simulateTransfer')) {
|
|
876
|
+
throw (new Error(`Asset movement provider ${scanStep.providerID} does not support simulateTransfer, which is required to compute valueOut for a persistent-forwarding step at ${convertAssetLocationToString(scanStep.from.location)}`));
|
|
877
|
+
}
|
|
878
|
+
const { signer: forwardedSigner } = await this.getAccountsForAction({
|
|
879
|
+
type: 'assetMovement',
|
|
880
|
+
providerMethod: 'initiateTransfer',
|
|
881
|
+
provider: forwardedProvider
|
|
882
|
+
}, this.#options?.overrides);
|
|
883
|
+
let persistentAddress;
|
|
884
|
+
if (await forwardedProvider.isOperationSupported('listPersistentForwarding')) {
|
|
885
|
+
try {
|
|
886
|
+
const existing = await forwardedProvider.listForwardingAddresses({
|
|
887
|
+
account: forwardedSigner,
|
|
888
|
+
search: [{
|
|
889
|
+
sourceLocation: scanStep.from.location,
|
|
890
|
+
destinationLocation: scanStep.to.location,
|
|
891
|
+
asset: scanStep.from.asset,
|
|
892
|
+
destinationAddress
|
|
893
|
+
}]
|
|
894
|
+
});
|
|
895
|
+
/*
|
|
896
|
+
* Filter to the exact address this step requires.
|
|
897
|
+
*/
|
|
898
|
+
const sourceLocationString = convertAssetLocationToString(scanStep.from.location);
|
|
899
|
+
const destLocationString = convertAssetLocationToString(scanStep.to.location);
|
|
900
|
+
const match = existing.addresses.find(addr => {
|
|
901
|
+
if (addr.destinationAddress !== destinationAddress) {
|
|
902
|
+
return (false);
|
|
903
|
+
}
|
|
904
|
+
if (!addr.sourceLocation || convertAssetLocationToString(addr.sourceLocation) !== sourceLocationString) {
|
|
905
|
+
return (false);
|
|
906
|
+
}
|
|
907
|
+
if (!addr.destinationLocation || convertAssetLocationToString(addr.destinationLocation) !== destLocationString) {
|
|
908
|
+
return (false);
|
|
909
|
+
}
|
|
910
|
+
return (true);
|
|
911
|
+
});
|
|
912
|
+
if (match) {
|
|
913
|
+
persistentAddress = match;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
catch (error) {
|
|
917
|
+
this.logger?.debug('AnchorChainingPlan::computePlan', `listForwardingAddresses lookup failed for step ${scanIndex}, will create a new address`, error);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (!persistentAddress) {
|
|
921
|
+
persistentAddress = await forwardedProvider.createPersistentForwardingAddress({
|
|
922
|
+
account: forwardedSigner,
|
|
923
|
+
sourceLocation: scanStep.from.location,
|
|
924
|
+
destinationLocation: scanStep.to.location,
|
|
925
|
+
destinationAddress,
|
|
926
|
+
asset: forwardedAssetPair
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
if (typeof persistentAddress.address !== 'string') {
|
|
930
|
+
throw (new Error(`Persistent forwarding address for step ${scanIndex} is not a resolved string (got ${typeof persistentAddress.address})`));
|
|
931
|
+
}
|
|
932
|
+
forwardedSteps.set(scanIndex, { provider: forwardedProvider, persistentAddress });
|
|
933
|
+
}
|
|
776
934
|
const stepPromises = [];
|
|
777
935
|
const resolvingSteps = new Set();
|
|
936
|
+
const precomputedValueOuts = new Map();
|
|
778
937
|
const resolveStep = async (index) => {
|
|
779
938
|
const step = this.path[index];
|
|
780
939
|
if (!step) {
|
|
781
940
|
throw (new Error(`Step ${index} is not defined`));
|
|
782
941
|
}
|
|
942
|
+
/*
|
|
943
|
+
* Detect cycles
|
|
944
|
+
*/
|
|
945
|
+
if (resolvingSteps.has(index)) {
|
|
946
|
+
throw (new Error(`Cyclic dependency detected in resolveStep: step ${index} is already being resolved`));
|
|
947
|
+
}
|
|
783
948
|
let promise = stepPromises[index];
|
|
784
949
|
if (!promise) {
|
|
785
950
|
resolvingSteps.add(index);
|
|
@@ -817,6 +982,9 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
817
982
|
throw (new Error(`Could not get FX quote/estimate for provider ${step.providerID}`));
|
|
818
983
|
}
|
|
819
984
|
const result = quotesOrEstimates[0];
|
|
985
|
+
if (!result.isQuote && result.estimate.canPerformExchange === false) {
|
|
986
|
+
throw (new Error(`FX estimate from provider ${step.providerID} indicates exchange cannot be performed`));
|
|
987
|
+
}
|
|
820
988
|
const convertedAmount = result.isQuote ? result.quote.convertedAmount : result.estimate.convertedAmount;
|
|
821
989
|
let valueIn;
|
|
822
990
|
let valueOut;
|
|
@@ -834,10 +1002,88 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
834
1002
|
return ({ type: 'fx', step, valueIn, valueOut, result });
|
|
835
1003
|
}
|
|
836
1004
|
else if (step.type === 'assetMovement') {
|
|
837
|
-
|
|
1005
|
+
if (affinity === 'to') {
|
|
1006
|
+
throw (new Error(`Chaining with affinity 'to' is not currently supported for asset movement steps, as it requires looking up transfer quotes/estimates which is not currently implemented`));
|
|
1007
|
+
}
|
|
1008
|
+
let depositValue;
|
|
1009
|
+
if (index === 0) {
|
|
1010
|
+
depositValue = affinityAndAmount.amount;
|
|
1011
|
+
}
|
|
1012
|
+
else {
|
|
1013
|
+
const precomputedPrev = precomputedValueOuts.get(index - 1);
|
|
1014
|
+
if (precomputedPrev !== undefined) {
|
|
1015
|
+
depositValue = precomputedPrev;
|
|
1016
|
+
}
|
|
1017
|
+
else {
|
|
1018
|
+
const previous = await resolveStep(index - 1);
|
|
1019
|
+
depositValue = previous.valueOut;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
const assetPair = { from: step.from.asset, to: step.to.asset };
|
|
1023
|
+
/*
|
|
1024
|
+
* Forwarded step: prior step deposits into a pre-resolved persistent address.
|
|
1025
|
+
*/
|
|
1026
|
+
const forwardedInfo = forwardedSteps.get(index);
|
|
1027
|
+
if (forwardedInfo) {
|
|
1028
|
+
const { provider: forwardedProvider, persistentAddress } = forwardedInfo;
|
|
1029
|
+
/*
|
|
1030
|
+
* Best-effort plan-time valueOut: simulateTransfer if available,
|
|
1031
|
+
* otherwise assume no rail fee.
|
|
1032
|
+
*/
|
|
1033
|
+
let estimatedValueOut = depositValue;
|
|
1034
|
+
if (await forwardedProvider.isOperationSupported('simulateTransfer')) {
|
|
1035
|
+
try {
|
|
1036
|
+
const { signer: forwardedSigner } = await this.getAccountsForAction({
|
|
1037
|
+
type: 'assetMovement',
|
|
1038
|
+
providerMethod: 'initiateTransfer',
|
|
1039
|
+
provider: forwardedProvider
|
|
1040
|
+
}, this.#options?.overrides);
|
|
1041
|
+
const simulated = await forwardedProvider.simulateTransfer({
|
|
1042
|
+
account: forwardedSigner,
|
|
1043
|
+
asset: assetPair,
|
|
1044
|
+
from: { location: step.from.location },
|
|
1045
|
+
to: { location: step.to.location },
|
|
1046
|
+
value: depositValue
|
|
1047
|
+
});
|
|
1048
|
+
const simulatedInstruction = simulated.instructions.find((instr) => instr.type === step.from.rail);
|
|
1049
|
+
let simulatedTotalReceive;
|
|
1050
|
+
if (simulatedInstruction) {
|
|
1051
|
+
simulatedTotalReceive = simulatedInstruction.totalReceiveAmount;
|
|
1052
|
+
if (simulatedTotalReceive === undefined && 'value' in simulatedInstruction) {
|
|
1053
|
+
simulatedTotalReceive = simulatedInstruction.value;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
if (simulatedTotalReceive !== undefined) {
|
|
1057
|
+
estimatedValueOut = BigInt(simulatedTotalReceive);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
catch (error) {
|
|
1061
|
+
this.logger?.debug('AnchorChainingPlan::resolveStep', `simulateTransfer for forwarded step ${index} valueOut estimation failed; falling back to depositValue`, error);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return ({
|
|
1065
|
+
type: 'forwarded',
|
|
1066
|
+
step,
|
|
1067
|
+
valueIn: depositValue,
|
|
1068
|
+
valueOut: estimatedValueOut,
|
|
1069
|
+
persistentAddress,
|
|
1070
|
+
provider: forwardedProvider
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
const providers = await assetMovementClient.getProvidersForTransfer({ asset: assetPair, from: step.from.location, to: step.to.location }, { providerIDs: [step.providerID] });
|
|
1074
|
+
if (!providers?.[0] || providers.length === 0) {
|
|
1075
|
+
throw (new Error(`Could not get asset movement provider ${step.providerID}`));
|
|
1076
|
+
}
|
|
1077
|
+
const provider = providers[0];
|
|
1078
|
+
const { signer } = await this.getAccountsForAction({
|
|
1079
|
+
type: 'assetMovement',
|
|
1080
|
+
providerMethod: 'initiateTransfer',
|
|
1081
|
+
provider
|
|
1082
|
+
}, this.#options?.overrides);
|
|
1083
|
+
let resolvedRecipient;
|
|
838
1084
|
let sendingToType;
|
|
839
1085
|
if (index === this.path.length - 1) {
|
|
840
|
-
|
|
1086
|
+
resolvedRecipient = this.request.destination.recipient;
|
|
841
1087
|
sendingToType = 'FINAL_DESTINATION';
|
|
842
1088
|
}
|
|
843
1089
|
else {
|
|
@@ -846,91 +1092,99 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
846
1092
|
if (!nextPathStep) {
|
|
847
1093
|
throw (new Error(`Expected next step at index ${index + 1} for asset movement step at index ${index}`));
|
|
848
1094
|
}
|
|
849
|
-
|
|
1095
|
+
/*
|
|
1096
|
+
* Next step is forwarded: recipient is its persistent address,
|
|
1097
|
+
* no need to resolve the next step's instructions.
|
|
1098
|
+
*/
|
|
1099
|
+
const nextForwardedInfo = forwardedSteps.get(index + 1);
|
|
1100
|
+
if (nextForwardedInfo) {
|
|
1101
|
+
const pfiAddress = nextForwardedInfo.persistentAddress.address;
|
|
1102
|
+
if (typeof pfiAddress !== 'string') {
|
|
1103
|
+
throw (new Error(`Persistent forwarding address for next step ${index + 1} is not a resolved string`));
|
|
1104
|
+
}
|
|
1105
|
+
resolvedRecipient = pfiAddress;
|
|
1106
|
+
}
|
|
1107
|
+
else if (nextPathStep.from.location === `chain:keeta:${this.parent['client'].network}`) {
|
|
850
1108
|
const { account } = await this.getAccountsForAction({
|
|
851
1109
|
type: 'assetMovement',
|
|
852
1110
|
providerMethod: 'initiateTransfer'
|
|
853
1111
|
}, this.#options?.overrides);
|
|
854
1112
|
// Store funds in-transit in the account instead of forwarding directly to provider.
|
|
855
|
-
|
|
1113
|
+
resolvedRecipient = account;
|
|
856
1114
|
}
|
|
857
1115
|
else {
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1116
|
+
/**
|
|
1117
|
+
* If the provider does not support simulateTransfer,
|
|
1118
|
+
* we cannot chain to this step.
|
|
1119
|
+
*/
|
|
1120
|
+
if (!await provider.isOperationSupported('simulateTransfer')) {
|
|
1121
|
+
throw (new Error(`Asset movement provider ${step.providerID} does not support simulateTransfer, which is required for chaining at non-keeta intermediate location ${convertAssetLocationToString(nextPathStep.from.location)}`));
|
|
1122
|
+
}
|
|
1123
|
+
const simulated = await provider.simulateTransfer({
|
|
1124
|
+
account: signer,
|
|
1125
|
+
asset: assetPair,
|
|
1126
|
+
from: { location: step.from.location },
|
|
1127
|
+
to: { location: step.to.location },
|
|
1128
|
+
value: depositValue
|
|
1129
|
+
});
|
|
1130
|
+
const simulatedInstruction = simulated.instructions.find((instr) => instr.type === step.from.rail);
|
|
1131
|
+
if (!simulatedInstruction) {
|
|
1132
|
+
throw (new Error(`Simulated transfer for step ${index} did not return an instruction matching rail ${step.from.rail}`));
|
|
1133
|
+
}
|
|
1134
|
+
let simulatedTotalReceive = simulatedInstruction.totalReceiveAmount;
|
|
1135
|
+
if (simulatedTotalReceive === undefined && 'value' in simulatedInstruction) {
|
|
1136
|
+
simulatedTotalReceive = simulatedInstruction.value;
|
|
1137
|
+
}
|
|
1138
|
+
if (simulatedTotalReceive === undefined) {
|
|
1139
|
+
throw (new Error(`totalReceiveAmount must be defined for simulated transfer when chaining`));
|
|
1140
|
+
}
|
|
1141
|
+
precomputedValueOuts.set(index, BigInt(simulatedTotalReceive));
|
|
1142
|
+
const nextStep = await resolveStep(index + 1);
|
|
1143
|
+
if (nextStep.type === 'assetMovement' || nextStep.type === 'keetaSend') {
|
|
1144
|
+
if (nextStep.usingInstruction.type !== step.to.rail) {
|
|
1145
|
+
throw (new Error(`Next step's usingInstruction type ${nextStep.usingInstruction.type} does not match expected ${step.to.rail} for recipient resolution`));
|
|
1146
|
+
}
|
|
1147
|
+
const foundInstruction = nextStep.usingInstruction;
|
|
1148
|
+
const isFiatPushRailFoundInstruction = (input) => {
|
|
1149
|
+
return (isFiatRail(input.type));
|
|
1150
|
+
};
|
|
1151
|
+
if (foundInstruction.type === 'KEETA_SEND') {
|
|
1152
|
+
throw (new Error(`Cannot currently chain from asset movement to KEETA_SEND step, as this implies multiple keeta locations in the path which is not currently supported`));
|
|
1153
|
+
}
|
|
1154
|
+
else if (isFiatPushRailFoundInstruction(foundInstruction)) {
|
|
1155
|
+
if (foundInstruction.depositMessage) {
|
|
1156
|
+
throw (new Error(`Deposit message outbound is not currently supported for chaining`));
|
|
879
1157
|
}
|
|
1158
|
+
resolvedRecipient = foundInstruction.account;
|
|
880
1159
|
}
|
|
881
|
-
else if (
|
|
882
|
-
|
|
1160
|
+
else if (foundInstruction.type === 'EVM_SEND') {
|
|
1161
|
+
resolvedRecipient = foundInstruction.sendToAddress;
|
|
883
1162
|
}
|
|
884
1163
|
else {
|
|
885
|
-
|
|
1164
|
+
throw (new Error(`Unsupported rail for chaining: ${step.to.rail}`));
|
|
886
1165
|
}
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
throw (new Error(`Chaining with affinity 'to' is not currently supported for asset movement steps, as it requires looking up transfer quotes/estimates which is not currently implemented`));
|
|
898
|
-
}
|
|
899
|
-
else {
|
|
900
|
-
if (index === 0) {
|
|
901
|
-
depositValue = affinityAndAmount.amount;
|
|
902
|
-
}
|
|
903
|
-
else {
|
|
904
|
-
const previous = await resolveStep(index - 1);
|
|
905
|
-
depositValue = previous.valueOut;
|
|
1166
|
+
}
|
|
1167
|
+
else if (nextStep.type === 'fx') {
|
|
1168
|
+
throw (new Error(`Cannot currently chain from asset movement to fx step, as fx step does not have recipient information`));
|
|
1169
|
+
}
|
|
1170
|
+
else if (nextStep.type === 'forwarded') {
|
|
1171
|
+
throw (new Error(`Internal invariant violation: forwarded step at index ${index + 1} reached simulate-cycle-break path; expected nextForwardedInfo branch to have handled it`));
|
|
1172
|
+
}
|
|
1173
|
+
else {
|
|
1174
|
+
assertNever(nextStep);
|
|
1175
|
+
}
|
|
906
1176
|
}
|
|
907
1177
|
}
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
}, this.#options?.overrides);
|
|
913
|
-
const transfer = await providers[0].initiateTransfer({
|
|
1178
|
+
const recipientString = KeetaNet.lib.Account.isInstance(resolvedRecipient)
|
|
1179
|
+
? resolvedRecipient.publicKeyString.get()
|
|
1180
|
+
: resolvedRecipient;
|
|
1181
|
+
const transfer = await provider.initiateTransfer({
|
|
914
1182
|
account: signer,
|
|
915
1183
|
asset: assetPair,
|
|
916
1184
|
from: { location: step.from.location },
|
|
917
1185
|
to: {
|
|
918
1186
|
location: step.to.location,
|
|
919
|
-
recipient:
|
|
920
|
-
let recipientResolved;
|
|
921
|
-
if (typeof recipient === 'function') {
|
|
922
|
-
recipientResolved = await recipient();
|
|
923
|
-
}
|
|
924
|
-
else {
|
|
925
|
-
recipientResolved = recipient;
|
|
926
|
-
}
|
|
927
|
-
if (KeetaNet.lib.Account.isInstance(recipientResolved)) {
|
|
928
|
-
return (recipientResolved.publicKeyString.get());
|
|
929
|
-
}
|
|
930
|
-
else {
|
|
931
|
-
return (recipientResolved);
|
|
932
|
-
}
|
|
933
|
-
})()
|
|
1187
|
+
recipient: recipientString
|
|
934
1188
|
},
|
|
935
1189
|
value: depositValue
|
|
936
1190
|
});
|
|
@@ -942,6 +1196,14 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
942
1196
|
if (totalReceiveAmount === undefined) {
|
|
943
1197
|
throw (new Error(`totalReceiveAmount must be defined for chaining`));
|
|
944
1198
|
}
|
|
1199
|
+
const actualValueOut = BigInt(totalReceiveAmount);
|
|
1200
|
+
// If we simulated to break a cycle, the next step's initiateTransfer was
|
|
1201
|
+
// keyed off the simulated valueOut; a mismatch here means the next step
|
|
1202
|
+
// is now misaligned, so fail at plan-time instead of letting execute() catch it.
|
|
1203
|
+
const simulatedValueOut = precomputedValueOuts.get(index);
|
|
1204
|
+
if (simulatedValueOut !== undefined && simulatedValueOut !== actualValueOut) {
|
|
1205
|
+
throw (new Error(`Simulated valueOut ${simulatedValueOut} for step ${index} does not match actual ${actualValueOut} from initiateTransfer`));
|
|
1206
|
+
}
|
|
945
1207
|
return ({
|
|
946
1208
|
type: 'assetMovement',
|
|
947
1209
|
step,
|
|
@@ -949,7 +1211,7 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
949
1211
|
usingInstruction: usingInstruction,
|
|
950
1212
|
transfer: transfer,
|
|
951
1213
|
sendingTo: sendingToType,
|
|
952
|
-
valueOut:
|
|
1214
|
+
valueOut: actualValueOut
|
|
953
1215
|
});
|
|
954
1216
|
}
|
|
955
1217
|
else if (step.type === 'keetaSend') {
|
|
@@ -1000,9 +1262,6 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
1000
1262
|
promise.then(() => resolvingSteps.delete(index), () => resolvingSteps.delete(index));
|
|
1001
1263
|
stepPromises[index] = promise;
|
|
1002
1264
|
}
|
|
1003
|
-
else if (resolvingSteps.has(index)) {
|
|
1004
|
-
throw (new Error(`Cyclic dependency detected in resolveStep: step ${index} is already being resolved`));
|
|
1005
|
-
}
|
|
1006
1265
|
return (await promise);
|
|
1007
1266
|
};
|
|
1008
1267
|
const steps = [];
|
|
@@ -1140,6 +1399,9 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
1140
1399
|
const timeoutMs = options?.timeoutMs ?? 300_000;
|
|
1141
1400
|
const deadline = Date.now() + timeoutMs;
|
|
1142
1401
|
while (true) {
|
|
1402
|
+
if (options?.abortSignal?.aborted) {
|
|
1403
|
+
throw (new Error(`Aborted while waiting for transfer ${transfer.transferId} to complete`));
|
|
1404
|
+
}
|
|
1143
1405
|
const status = await transfer.getTransferStatus();
|
|
1144
1406
|
if (status.transaction.status === 'COMPLETE') {
|
|
1145
1407
|
return (status);
|
|
@@ -1150,11 +1412,61 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
1150
1412
|
await KeetaNet.lib.Utils.Helper.asleep(intervalMs);
|
|
1151
1413
|
}
|
|
1152
1414
|
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Wait for the forwarded transfer the bridge creates after observing the
|
|
1417
|
+
* prior step's withdraw deposit in the persistent-forwarding address.
|
|
1418
|
+
*/
|
|
1419
|
+
async #pollForwardedTransaction(step, sourceTransaction, options) {
|
|
1420
|
+
const intervalMs = options?.intervalMs ?? 2000;
|
|
1421
|
+
const timeoutMs = options?.timeoutMs ?? 300_000;
|
|
1422
|
+
const deadline = Date.now() + timeoutMs;
|
|
1423
|
+
const { provider, persistentAddress } = step;
|
|
1424
|
+
const pfiAddress = persistentAddress.address;
|
|
1425
|
+
if (typeof pfiAddress !== 'string') {
|
|
1426
|
+
throw (new Error(`Persistent forwarding address must be a resolved string`));
|
|
1427
|
+
}
|
|
1428
|
+
const { account } = await this.getAccountsForAction({
|
|
1429
|
+
type: 'assetMovement',
|
|
1430
|
+
providerMethod: 'initiateTransfer',
|
|
1431
|
+
provider
|
|
1432
|
+
}, this.#options?.overrides);
|
|
1433
|
+
while (true) {
|
|
1434
|
+
if (options?.abortSignal?.aborted) {
|
|
1435
|
+
throw (new Error(`Aborted while waiting for forwarded transaction at ${pfiAddress} correlated to source tx ${sourceTransaction.transaction.id}`));
|
|
1436
|
+
}
|
|
1437
|
+
let transactions = [];
|
|
1438
|
+
try {
|
|
1439
|
+
const response = await provider.listTransactions({
|
|
1440
|
+
account,
|
|
1441
|
+
persistentAddresses: [{
|
|
1442
|
+
location: step.step.from.location,
|
|
1443
|
+
persistentAddress: pfiAddress
|
|
1444
|
+
}],
|
|
1445
|
+
transactions: [sourceTransaction]
|
|
1446
|
+
});
|
|
1447
|
+
transactions = response.transactions;
|
|
1448
|
+
}
|
|
1449
|
+
catch (error) {
|
|
1450
|
+
this.logger?.debug('AnchorChainingPlan::pollForwardedTransaction', `listTransactions failed for PersistentForwardingRelay address ${pfiAddress}`, error);
|
|
1451
|
+
}
|
|
1452
|
+
const candidate = transactions.find(tx => tx.status === 'COMPLETE');
|
|
1453
|
+
if (candidate) {
|
|
1454
|
+
return (candidate);
|
|
1455
|
+
}
|
|
1456
|
+
if (Date.now() >= deadline) {
|
|
1457
|
+
throw (new Error(`Timed out waiting for persistent-forwarding transaction at ${pfiAddress} correlated to source tx ${sourceTransaction.transaction.id}`));
|
|
1458
|
+
}
|
|
1459
|
+
await KeetaNet.lib.Utils.Helper.asleep(intervalMs);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1153
1462
|
async #pollExchangeStatus(exchange, options) {
|
|
1154
1463
|
const intervalMs = options?.intervalMs ?? 2000;
|
|
1155
1464
|
const timeoutMs = options?.timeoutMs ?? 300_000;
|
|
1156
1465
|
const deadline = Date.now() + timeoutMs;
|
|
1157
1466
|
while (true) {
|
|
1467
|
+
if (options?.abortSignal?.aborted) {
|
|
1468
|
+
throw (new Error(`Aborted while waiting for FX exchange ${exchange.exchange.exchangeID} to complete`));
|
|
1469
|
+
}
|
|
1158
1470
|
const status = await exchange.getExchangeStatus();
|
|
1159
1471
|
if (status.status === 'completed') {
|
|
1160
1472
|
return (status);
|
|
@@ -1174,11 +1486,23 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
1174
1486
|
}
|
|
1175
1487
|
const executedSteps = [];
|
|
1176
1488
|
this.#setState({ status: 'executing', completedSteps: [], currentStepIndex: 0 });
|
|
1177
|
-
|
|
1489
|
+
/*
|
|
1490
|
+
* Actual output value from each completed step, used for equality checking.
|
|
1491
|
+
*/
|
|
1178
1492
|
let prevActualValueOut = null;
|
|
1493
|
+
/**
|
|
1494
|
+
* Source-tx anchor for the next forwarded step's poll. Populated only
|
|
1495
|
+
* when the prior step is an asset-movement transfer that produced a
|
|
1496
|
+
* withdraw transaction on its destination chain; reset for any step
|
|
1497
|
+
* type that cannot deposit into a persistent-forwarding address.
|
|
1498
|
+
*/
|
|
1499
|
+
let prevWithdrawTx = null;
|
|
1179
1500
|
let index = 0;
|
|
1180
1501
|
try {
|
|
1181
1502
|
for (index = 0; index < this.plan.steps.length; index++) {
|
|
1503
|
+
if (options?.abortSignal?.aborted) {
|
|
1504
|
+
throw (new Error(`Execution aborted`));
|
|
1505
|
+
}
|
|
1182
1506
|
const onStepCompleted = (step) => {
|
|
1183
1507
|
executedSteps.push(step);
|
|
1184
1508
|
this.#emit('stepExecuted', step, index);
|
|
@@ -1193,16 +1517,32 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
1193
1517
|
// different amount than was negotiated in computeSteps.
|
|
1194
1518
|
if (index > 0 && prevActualValueOut !== null) {
|
|
1195
1519
|
if (prevActualValueOut !== step.valueIn) {
|
|
1196
|
-
|
|
1197
|
-
`expected ${step.valueIn} but previous step produced ${prevActualValueOut}`));
|
|
1520
|
+
if (prevActualValueOut < step.valueIn) {
|
|
1521
|
+
throw (new Error(`Execution failed at step ${index} due to value mismatch: expected at least ${step.valueIn} but previous step produced ${prevActualValueOut}`));
|
|
1522
|
+
}
|
|
1523
|
+
else {
|
|
1524
|
+
this.logger?.debug(`AnchorChainingPlan::execute`, `Value mismatch at step ${index} is non-critical since previous step produced more (${prevActualValueOut}) than expected (${step.valueIn}), proceeding with execution`);
|
|
1525
|
+
}
|
|
1198
1526
|
}
|
|
1199
1527
|
}
|
|
1200
1528
|
if (step.type === 'fx') {
|
|
1201
1529
|
const exchange = await step.result.createExchange();
|
|
1202
1530
|
await this.#pollExchangeStatus(exchange);
|
|
1203
1531
|
prevActualValueOut = step.valueOut;
|
|
1532
|
+
prevWithdrawTx = null;
|
|
1204
1533
|
onStepCompleted({ type: 'fx', plan: step, exchange });
|
|
1205
1534
|
}
|
|
1535
|
+
else if (step.type === 'forwarded') {
|
|
1536
|
+
if (!prevWithdrawTx) {
|
|
1537
|
+
throw (new Error(`Forwarded step at index ${index} requires the prior step to produce a withdraw transaction on its destination chain`));
|
|
1538
|
+
}
|
|
1539
|
+
const observed = await this.#pollForwardedTransaction(step, prevWithdrawTx, {
|
|
1540
|
+
...(options?.abortSignal ? { abortSignal: options.abortSignal } : {})
|
|
1541
|
+
});
|
|
1542
|
+
prevActualValueOut = BigInt(observed.to.value);
|
|
1543
|
+
prevWithdrawTx = null;
|
|
1544
|
+
onStepCompleted({ type: 'forwarded', plan: step, observedTransaction: observed });
|
|
1545
|
+
}
|
|
1206
1546
|
else if (step.type === 'assetMovement' || step.type === 'keetaSend') {
|
|
1207
1547
|
if (step.usingInstruction.type === 'KEETA_SEND') {
|
|
1208
1548
|
await this.#authorizedSend(options, step.usingInstruction.sendToAddress, BigInt(step.usingInstruction.value), KeetaNet.lib.Account.fromPublicKeyString(step.usingInstruction.tokenAddress).assertKeyType(KeetaNet.lib.Account.AccountKeyAlgorithm.TOKEN), step.usingInstruction.external);
|
|
@@ -1218,17 +1558,38 @@ export class AnchorChainingPlan extends AnchorChainingPath {
|
|
|
1218
1558
|
}
|
|
1219
1559
|
});
|
|
1220
1560
|
}
|
|
1561
|
+
else if (step.usingInstruction.type === 'EVM_SEND') {
|
|
1562
|
+
/* For EVM Sends for now we assume the last step sent to this address */
|
|
1563
|
+
this.logger?.debug(`AnchorChainingPlan::execute`, `Executing EVM_SEND instruction for step ${index} by sending to address ${step.usingInstruction.sendToAddress} with value ${step.usingInstruction.value} and token ${step.usingInstruction.tokenAddress}`);
|
|
1564
|
+
}
|
|
1221
1565
|
else {
|
|
1222
1566
|
throw (new Error(`Unsupported instruction type ${step.usingInstruction.type} for user-initiated transfer at step ${index}`));
|
|
1223
1567
|
}
|
|
1224
1568
|
if (step.type === 'assetMovement') {
|
|
1225
|
-
const status = await this.#pollTransferStatus(step.transfer
|
|
1569
|
+
const status = await this.#pollTransferStatus(step.transfer, {
|
|
1570
|
+
...(options?.abortSignal ? { abortSignal: options.abortSignal } : {})
|
|
1571
|
+
});
|
|
1226
1572
|
prevActualValueOut = BigInt(status.transaction.to.value);
|
|
1573
|
+
const withdraw = status.transaction.to.transactions.withdraw;
|
|
1574
|
+
if (withdraw) {
|
|
1575
|
+
prevWithdrawTx = {
|
|
1576
|
+
location: step.step.to.location,
|
|
1577
|
+
transaction: { id: withdraw.id }
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
else {
|
|
1581
|
+
prevWithdrawTx = null;
|
|
1582
|
+
}
|
|
1227
1583
|
onStepCompleted({ type: 'assetMovement', plan: step });
|
|
1228
1584
|
}
|
|
1229
1585
|
else if (step.type === 'keetaSend') {
|
|
1230
|
-
|
|
1586
|
+
/*
|
|
1587
|
+
* Direct Keeta send: optimistically treat as completed since
|
|
1588
|
+
* there is no provider transfer to poll. Cannot feed a forwarded
|
|
1589
|
+
* step because it does not produce a bridge withdraw.
|
|
1590
|
+
*/
|
|
1231
1591
|
prevActualValueOut = step.valueIn;
|
|
1592
|
+
prevWithdrawTx = null;
|
|
1232
1593
|
onStepCompleted({ type: 'keetaSend', plan: step });
|
|
1233
1594
|
}
|
|
1234
1595
|
else {
|
|
@@ -1309,6 +1670,20 @@ export class AnchorChaining {
|
|
|
1309
1670
|
else {
|
|
1310
1671
|
foundPaths = await this.graph.findPaths(input);
|
|
1311
1672
|
}
|
|
1673
|
+
// Filter out paths with non-chain steps in intermediate positions
|
|
1674
|
+
foundPaths = foundPaths?.filter(path => {
|
|
1675
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
1676
|
+
const item = path[i];
|
|
1677
|
+
if (!item) {
|
|
1678
|
+
continue;
|
|
1679
|
+
}
|
|
1680
|
+
const toLocation = toAssetLocation(item.to.location);
|
|
1681
|
+
if (toLocation.type !== 'chain' && i < path.length - 1) {
|
|
1682
|
+
return (false);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return (true);
|
|
1686
|
+
});
|
|
1312
1687
|
if (foundPaths.length === 0) {
|
|
1313
1688
|
return (null);
|
|
1314
1689
|
}
|
|
@@ -1323,13 +1698,46 @@ export class AnchorChaining {
|
|
|
1323
1698
|
if (!paths) {
|
|
1324
1699
|
return (null);
|
|
1325
1700
|
}
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
|
|
1701
|
+
const limit = options?.limit ?? 3;
|
|
1702
|
+
const sortedPaths = paths.sort((a, b) => a.path.length - b.path.length);
|
|
1703
|
+
let successCount = 0;
|
|
1704
|
+
let lowestStepsSuccessCount = Infinity;
|
|
1705
|
+
let lastAttemptedPathIdx = -1;
|
|
1706
|
+
const maxAttemptLoops = 3;
|
|
1707
|
+
let currentAttemptLoop = 0;
|
|
1708
|
+
const allOutput = [];
|
|
1709
|
+
while (successCount < limit && lastAttemptedPathIdx < sortedPaths.length - 1 && currentAttemptLoop < maxAttemptLoops) {
|
|
1710
|
+
currentAttemptLoop++;
|
|
1711
|
+
const pathsToTry = sortedPaths.slice(lastAttemptedPathIdx + 1, lastAttemptedPathIdx + 1 + (limit - successCount));
|
|
1712
|
+
if (pathsToTry.length === 0 || !pathsToTry[0]) {
|
|
1713
|
+
break;
|
|
1714
|
+
}
|
|
1715
|
+
if (pathsToTry[0].path.length > lowestStepsSuccessCount) {
|
|
1716
|
+
break;
|
|
1717
|
+
}
|
|
1718
|
+
const currentTry = await Promise.allSettled(pathsToTry.map(async function (path) {
|
|
1719
|
+
return (await AnchorChainingPlan.create(path, options));
|
|
1720
|
+
}));
|
|
1721
|
+
allOutput.push(...currentTry);
|
|
1722
|
+
for (let i = 0; i < currentTry.length; i++) {
|
|
1723
|
+
const result = currentTry[i];
|
|
1724
|
+
const path = pathsToTry[i];
|
|
1725
|
+
if (!result || !path) {
|
|
1726
|
+
continue;
|
|
1727
|
+
}
|
|
1728
|
+
if (result.status === 'fulfilled') {
|
|
1729
|
+
successCount++;
|
|
1730
|
+
if (path && path.path.length < lowestStepsSuccessCount) {
|
|
1731
|
+
lowestStepsSuccessCount = path.path.length;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
lastAttemptedPathIdx += pathsToTry.length;
|
|
1736
|
+
}
|
|
1329
1737
|
const ret = [];
|
|
1330
|
-
for (let i = 0; i <
|
|
1331
|
-
const path =
|
|
1332
|
-
const plan =
|
|
1738
|
+
for (let i = 0; i < allOutput.length; i++) {
|
|
1739
|
+
const path = sortedPaths[i];
|
|
1740
|
+
const plan = allOutput[i];
|
|
1333
1741
|
if (!path || !plan) {
|
|
1334
1742
|
continue;
|
|
1335
1743
|
}
|