Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bug with address dupplication #376

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
112 changes: 112 additions & 0 deletions examples/transactions/send-qi-paymentcode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
const quais = require('../../lib/commonjs/quais');
require('dotenv').config();

async function main() {
// Create provider
const options = {usePathing: false};
const provider = new quais.JsonRpcProvider(process.env.RPC_URL, undefined, options);

// Create wallet and connect to provider
console.log(process.env.RPC_URL)
const aliceMnemonic = quais.Mnemonic.fromPhrase(process.env.MNEMONIC);
const aliceWallet = quais.QiHDWallet.fromMnemonic(aliceMnemonic);
aliceWallet.connect(provider);

// Get Alice payment code
const alicePaymentCode = aliceWallet.getPaymentCode(0);
console.log("Alice payment code: ", alicePaymentCode);

// Create Bob wallet
const BOB_MNEMONIC = "innocent perfect bus miss prevent night oval position aspect nut angle usage expose grace juice";
const bobMnemonic = quais.Mnemonic.fromPhrase(BOB_MNEMONIC);
const bobWallet = quais.QiHDWallet.fromMnemonic(bobMnemonic);
bobWallet.connect(provider);

// Get Bob payment code
const bobPaymentCode = bobWallet.getPaymentCode(0);
console.log("Bob payment code: ", bobPaymentCode);

// Open channel
aliceWallet.openChannel(bobPaymentCode);
bobWallet.openChannel(alicePaymentCode);

// Scan Alice wallet
console.log("...scanning alice wallet");
await aliceWallet.scan(quais.Zone.Cyprus1);

// log alice change wallet addresses
console.log("Alice change wallet addresses: ", aliceWallet.getChangeAddressesForZone(quais.Zone.Cyprus1).map(a => a.address));
// log alice external wallet addresses
console.log("Alice external wallet addresses: ", aliceWallet.getAddressesForZone(quais.Zone.Cyprus1).map(a => a.address));

// Scan Bob wallet
console.log("...scanning bob wallet");
await bobWallet.scan(quais.Zone.Cyprus1);

// Get Alice initial balance
console.log("...getting alice initial balance");
const aliceInitialBalance = await aliceWallet.getBalanceForZone(quais.Zone.Cyprus1);
console.log("Alice initial balance: ", aliceInitialBalance);

// Get Bob initial balance
console.log("...getting bob initial balance");
const bobInitialBalance = await bobWallet.getBalanceForZone(quais.Zone.Cyprus1);
console.log("Bob initial balance: ", bobInitialBalance);

// Send Qi
console.log("...sending qi to Bob");
const amountToSendToBob = 25000;
const tx = await aliceWallet.sendTransaction(bobPaymentCode, amountToSendToBob, quais.Zone.Cyprus1, quais.Zone.Cyprus1);
console.log("... Alice transaction sent. Waiting for receipt...");

// Wait for tx to be mined
const txReceipt = await tx.wait();
console.log("Alice's transaction receipt: ", txReceipt);

// Sync wallets
console.log("...syncing wallets");
await aliceWallet.sync(quais.Zone.Cyprus1);
await bobWallet.sync(quais.Zone.Cyprus1);

// Get Alice updated balance
console.log("...getting alice updated balance");
const aliceUpdatedBalance = await aliceWallet.getBalanceForZone(quais.Zone.Cyprus1);
console.log("Alice updated balance: ", aliceUpdatedBalance);

// Get Bob updated balance
console.log("...getting bob updated balance");
const bobUpdatedBalance = await bobWallet.getBalanceForZone(quais.Zone.Cyprus1);
console.log("Bob updated balance: ", bobUpdatedBalance);

// Bob sends Qi back to Alice
console.log("...sending qi back to Alice");
const amountToSendToAlice = 10000;
const tx2 = await bobWallet.sendTransaction(alicePaymentCode, amountToSendToAlice, quais.Zone.Cyprus1, quais.Zone.Cyprus1);
console.log("... Bob sent transaction. Waiting for receipt...");

// Wait for tx2 to be mined
const tx2Receipt = await tx2.wait();
console.log("Bob's transaction receipt: ", tx2Receipt);

// Sync wallets
await aliceWallet.sync(quais.Zone.Cyprus1);
await bobWallet.sync(quais.Zone.Cyprus1);

// Get Alice updated balance
console.log("...getting alice updated balance");
const aliceUpdatedBalance2 = await aliceWallet.getBalanceForZone(quais.Zone.Cyprus1);
console.log("Alice updated balance: ", aliceUpdatedBalance2);

// Get Bob updated balance
console.log("...getting bob updated balance");
const bobUpdatedBalance2 = await bobWallet.getBalanceForZone(quais.Zone.Cyprus1);
console.log("Bob updated balance: ", bobUpdatedBalance2);

}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
50 changes: 49 additions & 1 deletion src/transaction/quai-transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ export class QuaiTransaction extends AbstractTransaction<Signature> implements Q
delete protoTx.etx_index;
delete protoTx.work_nonce;
delete protoTx.etx_type;
const protoTxCopy = structuredClone(protoTx);
const protoTxCopy = deepCopyProtoTransaction(protoTx);

if (protoTx.v && protoTx.r && protoTx.s) {
// check if protoTx.r is zero
Expand Down Expand Up @@ -539,3 +539,51 @@ export class QuaiTransaction extends AbstractTransaction<Signature> implements Q
return tx;
}
}

/**
* Deeply copies a ProtoTransaction object.
*
* @param {ProtoTransaction} proto - The ProtoTransaction object to copy.
* @returns {ProtoTransaction} The copied ProtoTransaction object.
*/
function deepCopyProtoTransaction(proto: ProtoTransaction): ProtoTransaction {
if (proto == null) return proto;

const copy: ProtoTransaction = {
type: proto.type,
chain_id: new Uint8Array(proto.chain_id),
nonce: proto.nonce,
};

// Handle optional Uint8Array fields
if (proto.to) copy.to = new Uint8Array(proto.to);
if (proto.value) copy.value = new Uint8Array(proto.value);
if (proto.data) copy.data = new Uint8Array(proto.data);
if (proto.gas_price) copy.gas_price = new Uint8Array(proto.gas_price);
if (proto.miner_tip) copy.miner_tip = new Uint8Array(proto.miner_tip);
if (proto.v) copy.v = new Uint8Array(proto.v);
if (proto.r) copy.r = new Uint8Array(proto.r);
if (proto.s) copy.s = new Uint8Array(proto.s);
if (proto.signature) copy.signature = new Uint8Array(proto.signature);
if (proto.etx_sender) copy.etx_sender = new Uint8Array(proto.etx_sender);

// Handle numeric fields
if (proto.gas !== undefined) copy.gas = proto.gas;
if (proto.etx_index !== undefined) copy.etx_index = proto.etx_index;
if (proto.work_nonce !== undefined) copy.work_nonce = proto.work_nonce;
if (proto.etx_type !== undefined) copy.etx_type = proto.etx_type;

// Handle access list
if (proto.access_list) {
copy.access_list = {
access_tuples: proto.access_list.access_tuples.map((tuple) => ({
address: new Uint8Array(tuple.address),
storage_key: tuple.storage_key.map((key) => ({
value: new Uint8Array(key.value),
})),
})),
};
}

return copy;
}
12 changes: 9 additions & 3 deletions src/wallet/qi-hdwallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1458,12 +1458,18 @@ export class QiHDWallet extends AbstractHDWallet<QiAddressInfo> {
}
}

// update addresses map
const updatedAddressesForMap = addresses.map((addr) => {
// Create a map to track unique addresses
const uniqueAddressMap = new Map<string, QiAddressInfo>();

// Process addresses in order, with updated addresses taking precedence
addresses.forEach((addr) => {
const updatedAddr = updatedAddresses.find((a) => a.address === addr.address);
return updatedAddr || addr;
uniqueAddressMap.set(addr.address, updatedAddr || addr);
});

// Convert map values back to array
const updatedAddressesForMap = Array.from(uniqueAddressMap.values());

this._addressesMap.set(path, updatedAddressesForMap);

const executeCreatedOutpointsCallback = async () => {
Expand Down
Loading