Skip to content

Commit

Permalink
refactor: transfer and transferfrom to save constrains for simpler ca…
Browse files Browse the repository at this point in the history
…ses (#7013)

Splits the `transfer` into `transfer` and `transfer_from`.
`transfer_from` behaves as our old `transfer` and is expected to be
useful for contracts pulling funds, while `transfer` is useful for
pushing funds around.

The `transfer` only take `to` and `amount` and don't have any authwit
implementation anymore. This means a reduction of a `call` for the case
where funds are pushed.
  • Loading branch information
LHerskind authored Jun 12, 2024
1 parent e673fd4 commit 612b972
Show file tree
Hide file tree
Showing 22 changed files with 68 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ contract AppSubscription {
) {
assert(tx_count as u64 <= SUBSCRIPTION_TXS as u64);

Token::at(storage.subscription_token_address.read_private()).transfer(
Token::at(storage.subscription_token_address.read_private()).transfer_from(
context.msg_sender(),
storage.subscription_recipient_address.read_private(),
storage.subscription_price.read_private(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ contract Crowdfunding {
// docs:start:do-transfer
// 2) Transfer the donation tokens from donor to this contract
let donor = context.msg_sender();
Token::at(storage.donation_token.read_private()).transfer(donor, context.this_address(), amount as Field, 0).call(&mut context);
Token::at(storage.donation_token.read_private()).transfer_from(donor, context.this_address(), amount as Field, 0).call(&mut context);
// docs:end:do-transfer

let header = context.get_header();
Expand All @@ -100,7 +100,7 @@ contract Crowdfunding {
assert(context.msg_sender() == operator_address, "Not an operator");

// 2) Transfer the donation tokens from this contract to the operator
Token::at(storage.donation_token.read_private()).transfer(context.this_address(), operator_address, amount as Field, 0).call(&mut context);
Token::at(storage.donation_token.read_private()).transfer(operator_address, amount as Field).call(&mut context);

// 3) Emit an unencrypted event so that anyone can audit how much the operator has withdrawn
let event = WithdrawalProcessed { amount, who: operator_address };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@ contract Escrow {
// Withdraws balance. Requires that msg.sender is the owner.
#[aztec(private)]
fn withdraw(token: AztecAddress, amount: Field, recipient: AztecAddress) {
let this = context.this_address();
let sender = context.msg_sender();

let note = storage.owner.get_note();
assert(note.address == sender);

Token::at(token).transfer(this, recipient, amount, 0).call(&mut context);
Token::at(token).transfer(recipient, amount).call(&mut context);
}
}
20 changes: 18 additions & 2 deletions noir-projects/noir-contracts/contracts/token_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,24 @@ contract Token {

// docs:start:transfer
#[aztec(private)]
fn transfer(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) {
fn transfer(to: AztecAddress, amount: Field) {
let from = context.msg_sender();

// By fetching the keys here, we can avoid doing an extra read from the storage, since from_ovpk would
// be needed twice.
let header = context.get_header();
let from_ovpk = header.get_ovpk_m(&mut context, from);
let from_ivpk = header.get_ivpk_m(&mut context, from);
let to_ivpk = header.get_ivpk_m(&mut context, to);

let amount = U128::from_integer(amount);
storage.balances.sub(from, amount).emit(encode_and_encrypt_with_keys(&mut context, from_ovpk, from_ivpk));
storage.balances.add(to, amount).emit(encode_and_encrypt_with_keys(&mut context, from_ovpk, to_ivpk));
}
// docs:end:transfer

#[aztec(private)]
fn transfer_from(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) {
// docs:start:assert_current_call_valid_authwit
if (!from.eq(context.msg_sender())) {
assert_current_call_valid_authwit(&mut context, from);
Expand All @@ -334,7 +351,6 @@ contract Token {
// docs:end:increase_private_balance
storage.balances.add(to, amount).emit(encode_and_encrypt_with_keys(&mut context, from_ovpk, to_ivpk));
}
// docs:end:transfer

// docs:start:burn
#[aztec(private)]
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec/src/examples/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ async function main() {

// We will now transfer tokens from Alice to Bob
logger.info(`Transferring ${TRANSFER_AMOUNT} tokens from Alice to Bob...`);
await tokenAlice.methods.transfer(alice, bob, TRANSFER_AMOUNT, 0).send().wait();
await tokenAlice.methods.transfer(bob, TRANSFER_AMOUNT).send().wait();

// Check the new balances
const aliceBalance = await tokenAlice.methods.balance_of_private(alice).simulate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe('benchmarks/proving', () => {

const fnCalls = [
(await getTokenContract(0)).methods.transfer_public(schnorrWalletAddress.address, recipient.address, 1000, 0),
(await getTokenContract(1)).methods.transfer(schnorrWalletAddress.address, recipient.address, 1000, 0),
(await getTokenContract(1)).methods.transfer(recipient.address, 1000),
// (await getTestContractOnPXE(2)).methods.emit_unencrypted(43),
// (await getTestContractOnPXE(3)).methods.create_l2_to_l1_message_public(45, 46, EthAddress.random()),
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe('benchmarks/tx_size_fees', () => {
const paymentMethod = createPaymentMethod();
const gasSettings = GasSettings.default();
const tx = await token.methods
.transfer(aliceWallet.getAddress(), bobAddress, 1n, 0)
.transfer(bobAddress, 1n)
.send({ fee: paymentMethod ? { gasSettings, paymentMethod } : undefined })
.wait();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ describe('Aztec persistence', () => {

const initialOwnerBalance = await contract.methods.balance_of_private(ownerWallet.getAddress()).simulate();

await contract.methods.transfer(ownerWallet.getAddress(), otherWallet.getAddress(), 500n, Fr.ZERO).send().wait();
await contract.methods.transfer(otherWallet.getAddress(), 500n).send().wait();

const [ownerBalance, targetBalance] = await Promise.all([
contract.methods.balance_of_private(ownerWallet.getAddress()).simulate(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ describe('e2e_sandbox_example', () => {
// We will now transfer tokens from ALice to Bob
const transferQuantity = 543n;
logger.info(`Transferring ${transferQuantity} tokens from Alice to Bob...`);
await tokenContractAlice.methods.transfer(alice, bob, transferQuantity, 0).send().wait();
await tokenContractAlice.methods.transfer(bob, transferQuantity).send().wait();

// Check the new balances
aliceBalance = await tokenContractAlice.methods.balance_of_private(alice).simulate();
Expand Down
25 changes: 5 additions & 20 deletions yarn-project/end-to-end/src/e2e_2_pxes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,7 @@ describe('e2e_2_pxes', () => {

// Transfer funds from A to B via PXE A
const contractWithWalletA = await TokenContract.at(token.address, walletA);
await contractWithWalletA.methods
.transfer(walletA.getAddress(), walletB.getAddress(), transferAmount1, 0)
.send()
.wait();
await contractWithWalletA.methods.transfer(walletB.getAddress(), transferAmount1).send().wait();

// Check balances and logs are as expected
await expectTokenBalance(walletA, token.address, walletA.getAddress(), initialBalance - transferAmount1);
Expand All @@ -151,10 +148,7 @@ describe('e2e_2_pxes', () => {

// Transfer funds from B to A via PXE B
const contractWithWalletB = await TokenContract.at(token.address, walletB);
await contractWithWalletB.methods
.transfer(walletB.getAddress(), walletA.getAddress(), transferAmount2, 0)
.send()
.wait({ interval: 0.1 });
await contractWithWalletB.methods.transfer(walletA.getAddress(), transferAmount2).send().wait({ interval: 0.1 });

// Check balances and logs are as expected
await expectTokenBalance(
Expand Down Expand Up @@ -281,10 +275,7 @@ describe('e2e_2_pxes', () => {

// Transfer funds from A to B via PXE A
const contractWithWalletA = await TokenContract.at(tokenAddress, walletA);
await contractWithWalletA.methods
.transfer(walletA.getAddress(), walletB.getAddress(), transferAmount1, 0)
.send()
.wait();
await contractWithWalletA.methods.transfer(walletB.getAddress(), transferAmount1).send().wait();

// now add the contract and check balances
await pxeB.registerContract(token);
Expand Down Expand Up @@ -316,17 +307,11 @@ describe('e2e_2_pxes', () => {

// Transfer funds from A to Shared Wallet via PXE A
const contractWithWalletA = await TokenContract.at(token.address, walletA);
await contractWithWalletA.methods
.transfer(walletA.getAddress(), sharedAccountAddress.address, transferAmount1, 0)
.send()
.wait();
await contractWithWalletA.methods.transfer(sharedAccountAddress.address, transferAmount1).send().wait();

// Now send funds from Shared Wallet to B via PXE A
const contractWithSharedWalletA = await TokenContract.at(token.address, sharedWalletOnA);
await contractWithSharedWalletA.methods
.transfer(sharedAccountAddress.address, walletB.getAddress(), transferAmount2, 0)
.send()
.wait();
await contractWithSharedWalletA.methods.transfer(walletB.getAddress(), transferAmount2).send().wait();

// check balances from PXE-A's perspective
await expectTokenBalance(walletA, token.address, walletA.getAddress(), initialBalance - transferAmount1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ describe('e2e_crowdfunding_and_claim', () => {
{
const action = donationToken
.withWallet(donorWallets[0])
.methods.transfer(donorWallets[0].getAddress(), crowdfundingContract.address, donationAmount, 0);
.methods.transfer_from(donorWallets[0].getAddress(), crowdfundingContract.address, donationAmount, 0);
const witness = await donorWallets[0].createAuthWit({ caller: crowdfundingContract.address, action });
await donorWallets[0].addAuthWitness(witness);
}
Expand Down Expand Up @@ -275,7 +275,7 @@ describe('e2e_crowdfunding_and_claim', () => {
{
const action = donationToken
.withWallet(donorWallets[1])
.methods.transfer(donorWallets[1].getAddress(), crowdfundingContract.address, donationAmount, 0);
.methods.transfer_from(donorWallets[1].getAddress(), crowdfundingContract.address, donationAmount, 0);
const witness = await donorWallets[1].createAuthWit({ caller: crowdfundingContract.address, action });
await donorWallets[1].addAuthWitness(witness);
}
Expand Down Expand Up @@ -368,7 +368,7 @@ describe('e2e_crowdfunding_and_claim', () => {
{
const action = donationToken
.withWallet(donorWallets[1])
.methods.transfer(donorWallets[1].getAddress(), crowdfundingContract.address, donationAmount, 0);
.methods.transfer_from(donorWallets[1].getAddress(), crowdfundingContract.address, donationAmount, 0);
const witness = await donorWallets[1].createAuthWit({ caller: crowdfundingContract.address, action });
await donorWallets[1].addAuthWitness(witness);
}
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_escrow_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ describe('e2e_escrow_contract', () => {
await expectBalance(owner, 50n);

const actions = [
token.methods.transfer(owner, recipient, 10, 0).request(),
token.methods.transfer(recipient, 10).request(),
escrowContract.methods.withdraw(token.address, 20, recipient).request(),
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ describe('e2e_fees dapp_subscription', () => {

async function subscribe(paymentMethod: FeePaymentMethod, blockDelta: number = 5, txCount: number = 4) {
const nonce = Fr.random();
const action = bananaCoin.methods.transfer(aliceAddress, bobAddress, t.SUBSCRIPTION_AMOUNT, nonce);
const action = bananaCoin.methods.transfer_from(aliceAddress, bobAddress, t.SUBSCRIPTION_AMOUNT, nonce);
await aliceWallet.createAuthWit({ caller: subscriptionContract.address, action });

return subscriptionContract
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('e2e_fees native_payments', () => {
it('sends tx with native fee payment method with no public calls', async () => {
const initialBalance = await gasTokenContract.methods.balance_of_public(aliceAddress).simulate();
const { transactionFee } = await bananaCoin.methods
.transfer(aliceAddress, bobAddress, 1n, 0n)
.transfer(bobAddress, 1n)
.send({ fee: { gasSettings, paymentMethod } })
.wait();
expect(transactionFee).toBeGreaterThan(0n);
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe('e2e_fees private_payment', () => {
* this is expected to squash notes and nullifiers
*/
const transferAmount = 5n;
const interaction = bananaCoin.methods.transfer(aliceAddress, bobAddress, transferAmount, 0n);
const interaction = bananaCoin.methods.transfer(bobAddress, transferAmount);
const localTx = await interaction.prove({
fee: {
gasSettings,
Expand Down Expand Up @@ -313,7 +313,7 @@ describe('e2e_fees private_payment', () => {
* create transparent note with RefundAmount
*/
const tx = await new BatchCall(aliceWallet, [
bananaCoin.methods.transfer(aliceAddress, bobAddress, privateTransfer, 0n).request(),
bananaCoin.methods.transfer(bobAddress, privateTransfer).request(),
bananaCoin.methods.shield(aliceAddress, shieldedBananas, shieldSecretHash, 0n).request(),
])
.send({
Expand Down
6 changes: 3 additions & 3 deletions yarn-project/end-to-end/src/e2e_key_rotation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ describe('e2e_key_rotation', () => {
const transfer1Amount = 654n;
{
({ txHash: txHashTransfer1 } = await contractWithWalletA.methods
.transfer(walletA.getAddress(), walletB.getAddress(), transfer1Amount, 0)
.transfer(walletB.getAddress(), transfer1Amount)
.send()
.wait());

Expand Down Expand Up @@ -190,7 +190,7 @@ describe('e2e_key_rotation', () => {
const transfer2Amount = 321n;
{
({ txHash: txHashTransfer2 } = await contractWithWalletA.methods
.transfer(walletA.getAddress(), walletB.getAddress(), transfer2Amount, 0)
.transfer(walletB.getAddress(), transfer2Amount)
.send()
.wait());

Expand Down Expand Up @@ -225,7 +225,7 @@ describe('e2e_key_rotation', () => {
// --> this way we verify that it's possible to obtain both keys via oracles
{
await contractWithWalletB.methods
.transfer(walletB.getAddress(), walletA.getAddress(), transfer1Amount + transfer2Amount, 0)
.transfer(walletA.getAddress(), transfer1Amount + transfer2Amount)
.send()
.wait();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ describe('e2e_multiple_accounts_1_enc_key', () => {

const contractWithWallet = await TokenContract.at(tokenAddress, wallets[senderIndex]);

await contractWithWallet.methods.transfer(sender, receiver, transferAmount, 0).send().wait();
await contractWithWallet.methods.transfer(receiver, transferAmount).send().wait();

for (let i = 0; i < expectedBalances.length; i++) {
await expectBalance(i, expectedBalances[i]);
Expand Down
9 changes: 2 additions & 7 deletions yarn-project/end-to-end/src/e2e_prover/full.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,7 @@ describe('full_prover', () => {
const privateBalance = await provenAssets[0].methods.balance_of_private(accounts[0].address).simulate();
const privateSendAmount = privateBalance / 2n;
expect(privateSendAmount).toBeGreaterThan(0n);
const privateInteraction = provenAssets[0].methods.transfer(
accounts[0].address,
accounts[1].address,
privateSendAmount,
0,
);
const privateInteraction = provenAssets[0].methods.transfer(accounts[1].address, privateSendAmount);

const publicBalance = await provenAssets[1].methods.balance_of_public(accounts[0].address).simulate();
const publicSendAmount = publicBalance / 2n;
Expand Down Expand Up @@ -94,7 +89,7 @@ describe('full_prover', () => {
);

it('rejects txs with invalid proofs', async () => {
const privateInteraction = t.fakeProofsAsset.methods.transfer(accounts[0].address, accounts[1].address, 1, 0);
const privateInteraction = t.fakeProofsAsset.methods.transfer(accounts[1].address, 1);
const publicInteraction = t.fakeProofsAsset.methods.transfer_public(accounts[0].address, accounts[1].address, 1, 0);

const sentPrivateTx = privateInteraction.send();
Expand Down
7 changes: 1 addition & 6 deletions yarn-project/end-to-end/src/e2e_prover/with_padding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,7 @@ describe('full_prover_with_padding_tx', () => {
const privateBalance = await provenAssets[0].methods.balance_of_private(accounts[0].address).simulate();
const privateSendAmount = privateBalance / 2n;
expect(privateSendAmount).toBeGreaterThan(0n);
const privateInteraction = provenAssets[0].methods.transfer(
accounts[0].address,
accounts[1].address,
privateSendAmount,
0,
);
const privateInteraction = provenAssets[0].methods.transfer(accounts[1].address, privateSendAmount);

const privateTx = await privateInteraction.prove();

Expand Down
Loading

0 comments on commit 612b972

Please sign in to comment.