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

ARC59 feedback based on beta testing #322

Merged
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
69 changes: 65 additions & 4 deletions assets/arc-0059/__test__/arc59.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import algosdk from 'algosdk';
import { Arc59Client } from '../contracts/clients/Arc59Client';

const fixture = algorandFixture();
algokit.Config.configure({ populateAppCallResources: true });
algokit.Config.configure({
populateAppCallResources: true,
// eslint-disable-next-line no-console
logger: { error: () => console.error, debug: () => {}, warn: console.warn, info: () => {}, verbose: () => {} },
});

/**
* Send an asset to a receiver using the ARC59 router
Expand All @@ -15,7 +19,6 @@ algokit.Config.configure({ populateAppCallResources: true });
* @param sender The address of the sender
* @param receiver The address of the receiver
* @param algorand The AlgorandClient instance to use to send transactions
* @param sendAlgoForNewAccount Whether to send 201_000 uALGO to the receiver so they can claim the asset with a 0-ALGO balance
*/
async function arc59SendAsset(
appClient: Arc59Client,
Expand Down Expand Up @@ -210,11 +213,21 @@ describe('Arc59', () => {
test('Brand new account getSendAssetInfo', async () => {
const res = await appClient.arc59GetSendAssetInfo({ asset: assetOne, receiver: algosdk.generateAccount().addr });

const itxns = res.return![0];
const mbr = res.return![1];
const [
itxns,
mbr,
routerOptedIn,
receiverOptedIn,
receiverAlgoNeededForClaim,
receiverAlgoNeededForWorstCaseClaim,
] = res.return!;

expect(itxns).toBe(5n);
expect(mbr).toBe(228_100n);
expect(routerOptedIn).toBe(true);
expect(receiverOptedIn).toBe(false);
expect(receiverAlgoNeededForClaim).toBe(201_000n);
expect(receiverAlgoNeededForWorstCaseClaim).toBe(201_000n);
});

test('Brand new account sendAsset', async () => {
Expand Down Expand Up @@ -269,4 +282,52 @@ describe('Arc59', () => {

expect(receiverAssetInfo.balance).toBe(1n);
});

test('two claims from 0-ALGO account', async () => {
const { algorand } = fixture;
const receiver = algorand.account.random();
await arc59SendAsset(appClient, assetOne, alice.addr, receiver.addr, algorand);
await arc59SendAsset(appClient, assetTwo, alice.addr, receiver.addr, algorand);

await arc59Claim(appClient, assetOne, receiver.addr, algorand);
await arc59Claim(appClient, assetTwo, receiver.addr, algorand);

const receiverAssetInfoOne = await algorand.account.getAssetInformation(receiver.addr, assetOne);
const receiverAssetInfoTwo = await algorand.account.getAssetInformation(receiver.addr, assetTwo);

expect(receiverAssetInfoOne.balance).toBe(1n);
expect(receiverAssetInfoTwo.balance).toBe(1n);
});

test('claim from abnormal ALGO balance', async () => {
const { algorand } = fixture;
const receiver = algorand.account.random();

await algorand.send.payment({ sender: alice.addr, receiver: receiver.addr, amount: algokit.microAlgos(123_456) });

await arc59SendAsset(appClient, assetOne, alice.addr, receiver.addr, algorand);
await arc59Claim(appClient, assetOne, receiver.addr, algorand);

const receiverAssetInfo = await algorand.account.getAssetInformation(receiver.addr, assetOne);

expect(receiverAssetInfo.balance).toBe(1n);
});

test('arc59GetSendAssetInfo with small amount of ALGO in inbox', async () => {
const { algorand } = fixture;
const receiver = algorand.account.random();

await arc59SendAsset(appClient, assetOne, alice.addr, receiver.addr, algorand);
await arc59Claim(appClient, assetOne, receiver.addr, algorand);

const inbox = (await appClient.arc59GetInbox({ receiver: receiver.addr })).return!;

await algorand.send.payment({
sender: alice.addr,
receiver: inbox,
amount: algokit.microAlgos(1),
});

await appClient.arc59GetSendAssetInfo({ asset: assetTwo, receiver: receiver.addr });
});
});
34 changes: 26 additions & 8 deletions assets/arc-0059/contracts/arc59.algo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type SendAssetInfo = {
receiverOptedIn: boolean;
/** The amount of ALGO the receiver would currently need to claim the asset */
receiverAlgoNeededForClaim: uint64;
/** The amount of ALGO the receiver would need if their balance dropped to 0 */
receiverAlgoNeededForWorstCaseClaim: uint64;
};

class ControlledAddress extends Contract {
Expand Down Expand Up @@ -90,6 +92,7 @@ export class ARC59 extends Contract {
routerOptedIn: routerOptedIn,
receiverOptedIn: receiverOptedIn,
receiverAlgoNeededForClaim: 0,
receiverAlgoNeededForWorstCaseClaim: globals.minBalance + globals.assetOptInMinBalance + globals.minTxnFee,
};

if (receiverOptedIn) return info;
Expand All @@ -98,7 +101,7 @@ export class ARC59 extends Contract {

// Determine how much ALGO the receiver needs to claim the asset
if (receiver.balance < algoNeededToClaim) {
info.receiverAlgoNeededForClaim += algoNeededToClaim - receiver.balance;
info.receiverAlgoNeededForClaim = algoNeededToClaim - receiver.balance;
}

// Add mbr and transaction for opting the router in
Expand Down Expand Up @@ -140,6 +143,21 @@ export class ARC59 extends Contract {
}
}

// If the inbox has extra ALGO, we need to account for that extra ALGO but also account for how much is used to claim the asset
if (inbox.balance > inbox.minBalance && info.receiverAlgoNeededForClaim !== 0) {
/**
* The total amount of ALGO needed up-front by the account when they claim the asset
* Add 1 txn for the upfront opt-in, 1 txn for the claim, 2 txns for the ALGO claim
*/
const algoConsumedByClaim = globals.assetOptInMinBalance + (info.itxns + 4) * globals.minTxnFee;
let inboxAlgoAvailable = inbox.balance > inbox.minBalance ? inbox.balance - inbox.minBalance : 0;
inboxAlgoAvailable = inboxAlgoAvailable > algoConsumedByClaim ? inboxAlgoAvailable - algoConsumedByClaim : 0;

if (inboxAlgoAvailable < info.receiverAlgoNeededForClaim) {
info.receiverAlgoNeededForClaim -= inboxAlgoAvailable;
}
}

return info;
}

Expand Down Expand Up @@ -178,13 +196,6 @@ export class ARC59 extends Contract {
const inboxExisted = this.inboxes(receiver).exists;
const inbox = this.arc59_getOrCreateInbox(receiver);

if (additionalReceiverFunds !== 0) {
sendPayment({
receiver: inbox,
amount: additionalReceiverFunds,
});
}

if (!inbox.isOptedInToAsset(axfer.xferAsset)) {
let inboxMbrDelta = globals.assetOptInMinBalance;
if (!inboxExisted) inboxMbrDelta += globals.minBalance;
Expand Down Expand Up @@ -213,6 +224,13 @@ export class ARC59 extends Contract {
xferAsset: axfer.xferAsset,
});

if (additionalReceiverFunds !== 0) {
sendPayment({
receiver: inbox,
amount: additionalReceiverFunds,
});
}

return inbox;
}

Expand Down
Loading
Loading