|
1 | 1 | import { BigNumber, Polymesh } from '@polymeshassociation/polymesh-sdk'; |
2 | | -import { KnownNftType, MetadataType, VenueType } from '@polymeshassociation/polymesh-sdk/types'; |
| 2 | +import { |
| 3 | + InstructionStatus, |
| 4 | + KnownNftType, |
| 5 | + MetadataType, |
| 6 | + VenueType, |
| 7 | +} from '@polymeshassociation/polymesh-sdk/types'; |
3 | 8 | import assert from 'node:assert'; |
4 | 9 |
|
5 | 10 | import { createAsset } from '~/sdk/assets/createAsset'; |
@@ -48,27 +53,31 @@ export const fungibleAssetControllerTransfer = async ( |
48 | 53 |
|
49 | 54 | await awaitMiddlewareSynced(transferTx, sdk, 30, 3000); |
50 | 55 |
|
51 | | - // affirm instruction |
| 56 | + // Affirm the instruction, if it still needs affirming. |
52 | 57 | // |
53 | | - // Blocks are 6s, so the old 10 x 2s window only covered about three of them. |
54 | | - // The counter party's pending list is served by the middleware, which lags |
55 | | - // the chain further under load, and on a busy CI runner three blocks was not |
56 | | - // reliably enough. 30 attempts covers ten blocks, still far inside the ~300s |
57 | | - // (50 block) validity `getPendingInstructionEndBlock` gives the instruction. |
58 | | - let counterInstruction; |
59 | | - for (let attempt = 0; attempt < 30; attempt++) { |
60 | | - const { pending } = await counterParty.getInstructions(); |
61 | | - counterInstruction = pending.find(({ id }) => id.eq(instruction.id)); |
62 | | - if (counterInstruction) { |
63 | | - break; |
| 58 | + // A receiver's affirmation is automatic unless they have opted in via |
| 59 | + // settlement.setMandatoryReceiverAffirmation, so this single leg instruction, |
| 60 | + // where the counter party is only receiving, settles on submission and never |
| 61 | + // appears in their pending list. Only reach for the pending instruction when |
| 62 | + // it has not already gone through. |
| 63 | + const { status } = await instruction.details(); |
| 64 | + |
| 65 | + if (status !== InstructionStatus.Success) { |
| 66 | + let counterInstruction; |
| 67 | + for (let attempt = 0; attempt < 10; attempt++) { |
| 68 | + const { pending } = await counterParty.getInstructions(); |
| 69 | + counterInstruction = pending.find(({ id }) => id.eq(instruction.id)); |
| 70 | + if (counterInstruction) { |
| 71 | + break; |
| 72 | + } |
| 73 | + await sleep(2000); |
64 | 74 | } |
65 | | - await sleep(2000); |
66 | | - } |
67 | | - assert(counterInstruction, 'the counter party should have the instruction as pending'); |
| 75 | + assert(counterInstruction, 'the counter party should have the instruction as pending'); |
68 | 76 |
|
69 | | - const affirmTx = await counterInstruction.affirm({}, { signingAccount: counterPartyAccount }); |
70 | | - await affirmTx.run(); |
71 | | - assert(affirmTx.isSuccess); |
| 77 | + const affirmTx = await counterInstruction.affirm({}, { signingAccount: counterPartyAccount }); |
| 78 | + await affirmTx.run(); |
| 79 | + assert(affirmTx.isSuccess); |
| 80 | + } |
72 | 81 |
|
73 | 82 | const controllerTransferTx = await asset.controllerTransfer({ |
74 | 83 | originPortfolio: targetDid, |
@@ -171,14 +180,20 @@ export const nonFungibleAssetControllerTransfer = async ( |
171 | 180 |
|
172 | 181 | await awaitMiddlewareSynced(transferTx, sdk); |
173 | 182 |
|
174 | | - // affirm instruction |
175 | | - const { pending } = await counterParty.getInstructions(); |
176 | | - const counterInstruction = pending.find(({ id }) => id.eq(instruction.id)); |
177 | | - assert(counterInstruction, 'the counter party should have the instruction as pending'); |
| 183 | + // Affirm the instruction, if it still needs affirming. See the note in |
| 184 | + // fungibleAssetControllerTransfer: a pure receiver affirms automatically, so |
| 185 | + // the instruction may already have settled. |
| 186 | + const { status } = await instruction.details(); |
178 | 187 |
|
179 | | - const affirmTx = await counterInstruction.affirm({}, { signingAccount: counterPartyAccount }); |
180 | | - await affirmTx.run(); |
181 | | - assert(affirmTx.isSuccess); |
| 188 | + if (status !== InstructionStatus.Success) { |
| 189 | + const { pending } = await counterParty.getInstructions(); |
| 190 | + const counterInstruction = pending.find(({ id }) => id.eq(instruction.id)); |
| 191 | + assert(counterInstruction, 'the counter party should have the instruction as pending'); |
| 192 | + |
| 193 | + const affirmTx = await counterInstruction.affirm({}, { signingAccount: counterPartyAccount }); |
| 194 | + await affirmTx.run(); |
| 195 | + assert(affirmTx.isSuccess); |
| 196 | + } |
182 | 197 |
|
183 | 198 | const controllerTransferTx = await collection.controllerTransfer({ |
184 | 199 | originPortfolio: targetDid, |
|
0 commit comments