Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions modules/sdk-coin-ton/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class Transaction extends BaseTransaction {
expireTime: number;
sender: string;
publicKey: string;
isV3ContractMessage: boolean;
protected unsignedMessage: string;
protected finalMessage: string;

Expand All @@ -26,6 +27,7 @@ export class Transaction extends BaseTransaction {
this.bounceable = false;
this.fromAddressBounceable = true;
this.toAddressBounceable = true;
this.isV3ContractMessage = false;
}

canSign(key: BaseKey): boolean {
Expand Down Expand Up @@ -93,7 +95,9 @@ export class Transaction extends BaseTransaction {
// expireAt should be set as per the provided arg value, regardless of the seqno
message.bits.writeUint(expireAt, 32);
message.bits.writeUint(seqno, 32);
message.bits.writeUint(0, 8); // op
if (!this.isV3ContractMessage) {
message.bits.writeUint(0, 8); // op
}
return message;
}

Expand Down Expand Up @@ -140,7 +144,7 @@ export class Transaction extends BaseTransaction {
body.writeCell(signingMessage);

let stateInit;
if (seqno === 0) {
if (seqno === 0 && !this.isV3ContractMessage) {
const WalletClass = TonWeb.Wallets.all['v4R2'];
const wallet = new WalletClass(new TonWeb.HttpProvider(), {
publicKey: TonWeb.utils.hexToBytes(this.publicKey),
Expand Down Expand Up @@ -266,10 +270,14 @@ export class Transaction extends BaseTransaction {
const seqno = slice.loadUint(32).toNumber();

const op = slice.loadUint(8).toNumber();
if (op !== 0) throw new Error('invalid op');
if (op !== 3) {
if (op !== 0) throw new Error('invalid op');

const sendMode = slice.loadUint(8).toNumber();
if (sendMode !== 3) throw new Error('invalid sendMode');
const sendMode = slice.loadUint(8).toNumber();
if (sendMode !== 3) throw new Error('invalid sendMode');
} else {
this.isV3ContractMessage = true;
}

let order = slice.loadRef();

Expand Down
5 changes: 5 additions & 0 deletions modules/sdk-coin-ton/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,9 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
this.transaction.publicKey = key;
return this;
}

isV3ContractMessage(bool: boolean): TransactionBuilder {
this.transaction.isV3ContractMessage = bool;
return this;
}
}
23 changes: 23 additions & 0 deletions modules/sdk-coin-ton/test/resources/ton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const privateKeys = {
prvKey4: 'ba4c313bcf830b825adaa3ae08cfde86e79e15a84e6fdc3b1fe35a6bb82d9f22',
prvKey5: 'fdb9ea1bb7f0120ce6eb7b047ac6744c4298f277756330e18dbd5419590a1ef2',
prvKey6: 'f66d30357e6a4180e9ab41865f0f347cbaffe60f9d499fe6c2f27046327652f6',
prvKey7: '339f9b7c8b039e1f71cc28c5fa30db29913624e2d53d4323a2664d3cfb5b07a4',
};

export const addresses = {
Expand All @@ -24,6 +25,13 @@ export const addresses = {
],
};

export const vestingContract = {
address: 'UQD0eii2CN6p_hznXcqBFlHHbcBQShW2Vj1EaIK2MnxVrWC6',
addressBounceable: 'EQD0eii2CN6p_hznXcqBFlHHbcBQShW2Vj1EaIK2MnxVrT1_',
publicKey: '339f9b7c8b039e1f71cc28c5fa30db29913624e2d53d4323a2664d3cfb5b07a4',
signature: '4K2iXQ5G5lU/1TKhfQn4xaAgL7sK91mFQb6OixkRwpZFJCYSbhmDJqLJfWRVRjfbL+cX4HjC7siqsGMDUgh4DQ==',
};

export const sender = {
address: 'UQAbJug-k-tufWMjEC1RKSM0iiJTDUcYkC7zWANHrkT55Afg',
publicKey: 'c0c3b9dc09932121ee351b2448c50a3ae2571b12951245c85f3bd95d5e7a06f8',
Expand Down Expand Up @@ -71,6 +79,21 @@ export const signedSendTransaction = {
},
};

export const v3CompatibleSignedSendTransaction = {
txBounceable:
'te6cckEBAgEAqAAB34gB6PRRbBG9U/w5zruVAiyjjtuAoJQrbKx6iNEFbGT4q1oHBW0S6HI3Mqn+qZUL6E/GLQEBfdhXuswqDfR0WMiOFLIpITCTcMwZNRZL6yKqMb7Zfzi/A8YXdkVVgxgakEPAaU1NGLtH0CDAAAAAGBwBAGZiAGcJlmF0UvErDsi5Rs21SP70rP1K36wtjBImqtbV96EuHMS0AAAAAAAAAAAAAAAAAAAiW72E',
txIdBounceable: '4i1GCyN5IkQQ-vESvNl4Wp1ejp7LfazRlNWzUbtGwSA=',
bounceableSignable: 'lOEOTzPXnPotTTHi7xgivFNUHH+xUgq/nKpaP/bK+Xo=',
recipient: {
address: 'EQDOEyzC6KXiVh2Rco2bapH96Vn6lb9YWxgkTVWtq-9CXL0m',
amount: '10000000',
},
recipientNonBounceable: {
address: 'UQDOEyzC6KXiVh2Rco2bapH96Vn6lb9YWxgkTVWtq-9CXODj',
amount: '10000000',
},
};

export const signedTokenSendTransaction = {
tx: 'te6cckECGgEABB0AAuGIAVSGb+UGjjP3lvt+zFA8wouI3McEd6CKbO2TwcZ3OfLKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmpoxdJlgLSAAAAAAADgEXAgE0AhYBFP8A9KQT9LzyyAsDAgEgBBECAUgFCALm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/pAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8H4AXTP8glghBwbHVnupI4MOMNA4IQZHN0crqSXwbjDQYHAHgB+gD0BDD4J28iMFAKoSG+8uBQghBwbHVngx6xcIAYUATLBSbPFlj6Ahn0AMtpF8sfUmDLPyDJgED7AAYAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gIBIAkQAgEgCg8CAVgLDAA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYAIBIA0OABmtznaiaEAga5Drhf/AABmvHfaiaEAQa5DrhY/AABG4yX7UTQ1wsfgAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/xITFBUAbtIH+gDU1CL5AAXIygcVy//J0Hd0gBjIywXLAiLPFlAF+gIUy2sSzMzJc/sAyEAUgQEI9FHypwIAcIEBCNcY+gDTP8hUIEeBAQj0UfKnghBub3RlcHSAGMjLBcsCUAbPFlAE+gIUy2oSyx/LP8lz+wACAGyBAQjXGPoA0z8wUiSBAQj0WfKnghBkc3RycHSAGMjLBcsCUAXPFlAD+gITy2rLHxLLP8lz+wAACvQAye1UAFEAAAAAKamjF9NTAQHUHhbX00VGZ3d2r8hbJxuz7PaxmuCOJ6kgckppQAFmQgABT9LR3Iqffskp0J9gWYO8Azlnb33BCMj8FqIUIGxGOZpiWgAAAAAAAAAAAAAAAAABGAGuD4p+pQAAAAAAAAAAQ7msoAgA/BGdBi/R01erquxJOvPgGKclBawUs3MAi0/IdctKQz8AKpDN/KDRxn7y32/ZigeYUXEbmOCO9BFNnbJ4OM7nPllGHoSBGQAkAAAAAGpldHRvbiB0ZXN0aW5nwHtw7A==',
tx2: 'te6cckECGgEABBQAAuGIAVSGb+UGjjP3lvt+zFA8wouI3McEd6CKbO2TwcZ3OfLKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmpoxdJlgLSAAAAAAADgEXAgE0AhYBFP8A9KQT9LzyyAsDAgEgBBECAUgFCALm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/pAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8H4AXTP8glghBwbHVnupI4MOMNA4IQZHN0crqSXwbjDQYHAHgB+gD0BDD4J28iMFAKoSG+8uBQghBwbHVngx6xcIAYUATLBSbPFlj6Ahn0AMtpF8sfUmDLPyDJgED7AAYAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gIBIAkQAgEgCg8CAVgLDAA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYAIBIA0OABmtznaiaEAga5Drhf/AABmvHfaiaEAQa5DrhY/AABG4yX7UTQ1wsfgAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/xITFBUAbtIH+gDU1CL5AAXIygcVy//J0Hd0gBjIywXLAiLPFlAF+gIUy2sSzMzJc/sAyEAUgQEI9FHypwIAcIEBCNcY+gDTP8hUIEeBAQj0UfKnghBub3RlcHSAGMjLBcsCUAbPFlAE+gIUy2oSyx/LP8lz+wACAGyBAQjXGPoA0z8wUiSBAQj0WfKnghBkc3RycHSAGMjLBcsCUAXPFlAD+gITy2rLHxLLP8lz+wAACvQAye1UAFEAAAAAKamjF9NTAQHUHhbX00VGZ3d2r8hbJxuz7PaxmuCOJ6kgckppQAFoYgABT9LR3Iqffskp0J9gWYO8Azlnb33BCMj8FqIUIGxGOaAX14QAAAAAAAAAAAAAAAAAARgBrg+KfqUAAAAAAAAAAEO5rKAIAPwRnQYv0dNXq6rsSTrz4BinJQWsFLNzAItPyHXLSkM/ACqQzfyg0cZ+8t9v2YoHmFFxG5jgjvQRTZ2yeDjO5z5ZRzEtARkAEAAAAAB0ZXN0MfeoHg==',
Expand Down
47 changes: 47 additions & 0 deletions modules/sdk-coin-ton/test/unit/transferBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,51 @@ describe('Ton Transfer Builder', () => {
const txJson2 = tx2.toJson();
txJson2.destinationAlias.should.equal(otherFormat);
});

it('should build a transfer tx compatible with v3 wallet contract', async function () {
const txBuilder = factory.getTransferBuilder();
txBuilder.sender(testData.vestingContract.address);
txBuilder.sequenceNumber(3);
txBuilder.expireTime(1761215512);
txBuilder.send(testData.v3CompatibleSignedSendTransaction.recipient);
txBuilder.isV3ContractMessage(true);
txBuilder.bounceable(true);
txBuilder.addSignature(
{ pub: testData.vestingContract.publicKey },
Buffer.from(testData.vestingContract.signature, 'base64')
);
const tx = await txBuilder.build();
should.equal(tx.type, TransactionType.Send);
tx.inputs.length.should.equal(1);
tx.inputs[0].should.deepEqual({
address: testData.vestingContract.address,
value: testData.v3CompatibleSignedSendTransaction.recipient.amount,
coin: 'tton',
});
tx.outputs.length.should.equal(1);
tx.outputs[0].should.deepEqual({
address: testData.v3CompatibleSignedSendTransaction.recipient.address,
value: testData.v3CompatibleSignedSendTransaction.recipient.amount,
coin: 'tton',
});
});

it('should build a v3 compatible send transaction using raw transaction hex', async function () {
const txBuilder = factory.from(testData.v3CompatibleSignedSendTransaction.txBounceable);
const builtTx = await txBuilder.build();
const jsonTx = builtTx.toJson();
should.equal(builtTx.type, TransactionType.Send);
should.equal(
builtTx.signablePayload.toString('base64'),
testData.v3CompatibleSignedSendTransaction.bounceableSignable
);
should.equal(builtTx.id, testData.v3CompatibleSignedSendTransaction.txIdBounceable);
jsonTx.sender.should.equal(testData.vestingContract.addressBounceable);
jsonTx.destination.should.equal(testData.v3CompatibleSignedSendTransaction.recipient.address);
jsonTx.destinationAlias.should.equal(testData.v3CompatibleSignedSendTransaction.recipientNonBounceable.address);
jsonTx.amount.should.equal(testData.v3CompatibleSignedSendTransaction.recipient.amount);
jsonTx.seqno.should.equal(3);
jsonTx.expirationTime.should.equal(1761215512);
jsonTx.bounceable.should.equal(true);
});
});