Skip to content

Commit 4b99418

Browse files
committed
fix(sdk-coin-ada): token verifytransaction
TICKET: COIN-7292
1 parent 0df29c7 commit 4b99418

File tree

2 files changed

+511
-2
lines changed

2 files changed

+511
-2
lines changed

modules/sdk-coin-ada/src/adaToken.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import { Ada } from './ada';
2-
import { BitGoBase, CoinConstructor, NamedCoinConstructor } from '@bitgo/sdk-core';
2+
import {
3+
BitGoBase,
4+
CoinConstructor,
5+
NamedCoinConstructor,
6+
VerifyTransactionOptions,
7+
NodeEnvironmentError,
8+
} from '@bitgo/sdk-core';
39
import { coins, tokens, AdaTokenConfig } from '@bitgo/statics';
10+
import { Transaction } from './lib';
11+
import * as CardanoWasm from '@emurgo/cardano-serialization-lib-nodejs';
12+
import assert from 'assert';
413

514
export class AdaToken extends Ada {
615
public readonly tokenConfig: AdaTokenConfig;
@@ -85,4 +94,72 @@ export class AdaToken extends Ada {
8594
get contractAddress() {
8695
return this.tokenConfig.contractAddress;
8796
}
97+
98+
/**
99+
* Verify that a token transaction prebuild complies with the original intention.
100+
* For token transfers, we need to verify the token amount in multiAssets, not the ADA amount.
101+
* For token consolidation, we verify all outputs go to the base address.
102+
*
103+
* @param params.txPrebuild prebuild transaction
104+
* @param params.txParams transaction parameters
105+
* @param params.verification verification options (includes consolidationToBaseAddress flag)
106+
* @param params.wallet wallet object for getting base address
107+
* @return true if verification success
108+
*/
109+
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
110+
try {
111+
const coinConfig = coins.get(this.getBaseChain());
112+
const { txPrebuild, txParams, verification, wallet } = params;
113+
const transaction = new Transaction(coinConfig);
114+
assert(txPrebuild.txHex, new Error('missing required tx prebuild property txHex'));
115+
116+
transaction.fromRawTransaction(txPrebuild.txHex);
117+
const txJson = transaction.toJson();
118+
119+
if (txParams.recipients !== undefined) {
120+
// assetName in tokenConfig is ASCII (e.g. 'WATER'), convert to hex for comparison
121+
const asciiEncodedAssetName = Buffer.from(this.tokenConfig.assetName).toString('hex');
122+
123+
// ASCII encoded asset name may be appended to the policy ID (consistent with crypto compare)
124+
// But cardano sdk requires only the policy ID (28 bytes = 56 hex chars) for ScriptHash
125+
let policyId = this.tokenConfig.policyId;
126+
if (policyId.endsWith(asciiEncodedAssetName)) {
127+
policyId = policyId.substring(0, policyId.length - asciiEncodedAssetName.length);
128+
}
129+
130+
const policyScriptHash = CardanoWasm.ScriptHash.from_hex(policyId);
131+
const assetName = CardanoWasm.AssetName.new(Buffer.from(asciiEncodedAssetName, 'hex'));
132+
133+
for (const recipient of txParams.recipients) {
134+
const found = txJson.outputs.some((output) => {
135+
if (recipient.address !== output.address || !output.multiAssets) {
136+
return false;
137+
}
138+
const multiAssets = output.multiAssets as CardanoWasm.MultiAsset;
139+
const tokenQty = multiAssets.get_asset(policyScriptHash, assetName);
140+
return tokenQty && tokenQty.to_str() === recipient.amount;
141+
});
142+
143+
if (!found) {
144+
throw new Error('cannot find recipient in expected output');
145+
}
146+
}
147+
} else if (verification?.consolidationToBaseAddress) {
148+
// For token consolidation, verify all outputs go to the base address
149+
const baseAddress = wallet?.coinSpecific()?.baseAddress || wallet?.coinSpecific()?.rootAddress;
150+
151+
for (const output of txJson.outputs) {
152+
if (output.address !== baseAddress) {
153+
throw new Error('tx outputs does not match with expected address');
154+
}
155+
}
156+
}
157+
} catch (e) {
158+
if (e instanceof NodeEnvironmentError) {
159+
return true;
160+
}
161+
throw e;
162+
}
163+
return true;
164+
}
88165
}

0 commit comments

Comments
 (0)