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
8 changes: 2 additions & 6 deletions modules/sdk-coin-polyx/src/polyx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class Polyx extends SubstrateCoin {

protected async getFee(destAddr: string, srcAddr: string, amount: number): Promise<number> {
const api = await this.getInitializedNodeAPI();
const info = await api.tx.balances.transfer(destAddr, amount).paymentInfo(srcAddr);
const info = await api.tx.balances.transferAllowDeath(destAddr, amount).paymentInfo(srcAddr);
return info.partialFee.toNumber();
}

Expand Down Expand Up @@ -195,7 +195,6 @@ export class Polyx extends SubstrateCoin {
assert(params.walletPassphrase, 'missing wallet passphrase');

const signingMaterial = await this.getEddsaSigningMaterial(params.userKey, params.walletPassphrase);
const ED25519_PREFIX = 0x00;
const substrateKeyPair = new SubstrateKeyPair({ pub: accountId });
if (signingMaterial.version === 'v2') {
const rawSig = await this.signSubstrateMpcV2Recovery({
Expand All @@ -207,10 +206,7 @@ export class Polyx extends SubstrateCoin {
derivationPath: currPath,
bitgo: this.bitgo,
});
txBuilder.addSignature(
{ pub: substrateKeyPair.getKeys().pub },
Buffer.concat([Buffer.from([ED25519_PREFIX]), rawSig])
);
txBuilder.addSignature({ pub: substrateKeyPair.getKeys().pub }, rawSig);
} else {
const userSigningMaterial = JSON.parse(signingMaterial.userPrv) as EDDSAMethodTypes.UserSigningMaterial;
const backupPrv = await this.bitgo.decrypt({
Expand Down
26 changes: 23 additions & 3 deletions modules/sdk-coin-polyx/test/unit/polyx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,11 @@ describe('Polyx:', function () {
(result.serializedTx as string).should.be.a.String().and.not.be.empty();
sandBox.assert.notCalled(getTSSSignatureSpy);

// Substrate MultiSignature Ed25519 discriminant (0x00) must prefix the 64-byte signature.
sandBox.assert.calledOnce(addSignatureSpy);
const signature: Buffer = addSignatureSpy.firstCall.args[1];
signature.length.should.equal(65);
signature[0].should.equal(0x00);
// constructSignedPayload prepends the 0x00 Ed25519 discriminant internally;
// recover() must pass the raw 64-byte signature unchanged.
signature.length.should.equal(64);
});

it('should produce a cryptographically valid Ed25519 signature', async function () {
Expand Down Expand Up @@ -331,6 +331,26 @@ describe('Polyx:', function () {
const paramsWithoutPassphrase = { ...mpcV2RecoverParams, walletPassphrase: undefined };
await baseCoin.recover(paramsWithoutPassphrase).should.be.rejectedWith('missing wallet passphrase');
});

it('should pass the raw 64-byte signature to addSignature on MPCv2 path', async function () {
// Regression: previously wrapped rawSig with a manual Ed25519 discriminant (0x00)
// before addSignature. constructSignedPayload already prepends that discriminant,
// so wrapping here caused a double prefix that shifted the on-wire signature by
// one byte, dropping the last byte of sigma and producing
// `1010: Bad signature` on-chain.
const rawSig = Buffer.alloc(64, 0xab);
sandBox
.stub(baseCoin as unknown as { signSubstrateMpcV2Recovery: unknown }, 'signSubstrateMpcV2Recovery')
.resolves(rawSig);
const addSignatureSpy = sandBox.spy(TransferBuilder.prototype, 'addSignature');

await baseCoin.recover(mpcV2RecoverParams);

sandBox.assert.calledOnce(addSignatureSpy);
const sig: Buffer = addSignatureSpy.firstCall.args[1];
sig.length.should.equal(64);
sig.should.deepEqual(rawSig);
});
});
});

Expand Down
Loading