Skip to content

Commit

Permalink
Initial ARC-4 support
Browse files Browse the repository at this point in the history
 Co-authored-by: Daniel McGregor <[email protected]>
 Co-authored-by: Tristan Menzel <[email protected]>
  • Loading branch information
achidlow committed Dec 8, 2023
1 parent 00b8626 commit 08d87c5
Show file tree
Hide file tree
Showing 762 changed files with 96,439 additions and 15,365 deletions.
628 changes: 628 additions & 0 deletions examples/TEALScript/algopy.log

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions examples/TEALScript/auction/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Port of Beaker auction contract to TEALScript. Original source: https://github.com/algorand-devrel/beaker-auction/tree/7e1fe62b852c0d819954a931f10cf39d841cbc02


502 changes: 502 additions & 0 deletions examples/TEALScript/auction/algopy.log

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions examples/TEALScript/auction/auction.algo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { Contract } from '../../src/lib/index';

// eslint-disable-next-line no-unused-vars
class Auction extends Contract {
previousBidder = GlobalStateKey<Address>();

auctionEnd = GlobalStateKey<uint64>();

previousBid = GlobalStateKey<uint64>();

asaAmt = GlobalStateKey<uint64>();

asa = GlobalStateKey<Asset>();

claimableAmount = LocalStateKey<uint64>();

createApplication(): void {
this.auctionEnd.value = 0;
this.previousBid.value = 0;
this.asaAmt.value = 0;
this.asa.value = Asset.zeroIndex;

// Use zero address rather than an empty string for Account type safety
this.previousBidder.value = globals.zeroAddress;
}

optIntoAsset(asset: Asset): void {
/// Only allow app creator to opt the app account into a ASA
verifyTxn(this.txn, { sender: globals.creatorAddress });

/// Verify a ASA hasn't already been opted into
assert(this.asa.value === Asset.zeroIndex);

/// Save ASA ID in global state
this.asa.value = asset;

/// Submit opt-in transaction: 0 asset transfer to self
sendAssetTransfer({
assetReceiver: this.app.address,
xferAsset: asset,
assetAmount: 0,
});
}

startAuction(startingPrice: uint64, length: uint64, axfer: AssetTransferTxn): void {
verifyTxn(this.txn, { sender: globals.creatorAddress });

/// Ensure the auction hasn't already been started
assert(this.auctionEnd.value === 0);

/// Verify axfer
verifyTxn(axfer, { assetReceiver: this.app.address });

/// Set global state
this.asaAmt.value = axfer.assetAmount;
this.auctionEnd.value = globals.latestTimestamp + length;
this.previousBid.value = startingPrice;
}

private pay(receiver: Account, amount: uint64): void {
sendPayment({
receiver: receiver,
amount: amount,
});
}

optInToApplication(): void {}

// eslint-disable-next-line no-unused-vars
bid(payment: PayTxn): void {
/// Ensure auction hasn't ended
assert(globals.latestTimestamp < this.auctionEnd.value);

/// Verify payment transaction
verifyTxn(payment, {
sender: this.txn.sender,
amount: { greaterThan: this.previousBid.value },
});

/// Set global state
this.previousBid.value = payment.amount;
this.previousBidder.value = payment.sender;

/// Update claimable amount
this.claimableAmount(this.txn.sender).value = payment.amount;
}

claimBids(): void {
const originalAmount = this.claimableAmount(this.txn.sender).value;
let amount = originalAmount;

/// subtract previous bid if sender is previous bidder
if (this.txn.sender === this.previousBidder.value) amount = amount - this.previousBid.value;

this.pay(this.txn.sender, amount);
this.claimableAmount(this.txn.sender).value = originalAmount - amount;
}

claim_asset(asset: Asset): void {
assert(globals.latestTimestamp > this.auctionEnd.value);

/// Send ASA to previous bidder
sendAssetTransfer({
assetReceiver: this.previousBidder.value,
xferAsset: asset,
assetAmount: this.asaAmt.value,
assetCloseTo: this.previousBidder.value,
});
}

deleteApplication(): void {
sendPayment({
receiver: globals.creatorAddress,
closeRemainderTo: globals.creatorAddress,
amount: 0,
});
}
}
128 changes: 128 additions & 0 deletions examples/TEALScript/auction/contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from algopy import (
ARC4Contract,
AssetTransferTransaction,
CreateInnerTransaction,
Global,
Local,
PaymentTransaction,
Transaction,
TransactionType,
UInt64,
arc4,
subroutine,
)
from algopy._reference import Asset


class Auction(ARC4Contract):
def __init__(self) -> None:
self.auction_end = UInt64(0)
self.previous_bid = UInt64(0)
self.asa_amount = UInt64(0)
self.asa = Asset(0)
# Use zero address rather than an empty string for Account type safety
self.previous_bidder = Global.zero_address()
self.claimable_amount = Local(UInt64)

@arc4.abimethod
def opt_into_asset(self, asset: Asset) -> None:
# Only allow app creator to opt the app account into a ASA
assert Transaction.sender() == Global.creator_address(), "Only creator can opt in to ASA"
# Verify a ASA hasn't already been opted into
assert self.asa.asset_id == 0, "ASA already opted in"
# Save ASA ID in global state
self.asa = asset

# Submit opt-in transaction: 0 asset transfer to self
CreateInnerTransaction.begin()
CreateInnerTransaction.set_type_enum(TransactionType.AssetTransfer)
CreateInnerTransaction.set_fee(UInt64(0)) # cover fee with outer txn
CreateInnerTransaction.set_asset_receiver(Global.current_application_address())
CreateInnerTransaction.set_xfer_asset(asset.asset_id)
CreateInnerTransaction.submit()

@arc4.abimethod
def start_auction(
self, starting_price: arc4.UInt64, length: arc4.UInt64, axfer: AssetTransferTransaction
) -> None:
assert (
Transaction.sender() == Global.creator_address()
), "auction must be started by creator"

# Ensure the auction hasn't already been started
assert self.auction_end == 0, "auction already started"

# Verify axfer
assert (
axfer.asset_receiver == Global.current_application_address()
), "axfer must transfer to this app"

# Set global state
self.asa_amount = axfer.asset_amount
self.auction_end = Global.latest_timestamp() + length.decode()
self.previous_bid = starting_price.decode()

@arc4.abimethod
def opt_in(self) -> None:
pass

@arc4.abimethod
def bid(self, pay: PaymentTransaction) -> None:
# Ensure auction hasn't ended
assert Global.latest_timestamp() < self.auction_end, "auction has ended"

# Verify payment transaction
assert pay.sender == Transaction.sender(), "payment sender must match transaction sender"
assert pay.amount > self.previous_bid, "Bid must be higher than previous bid"

# set global state
self.previous_bid = pay.amount
self.previous_bidder = pay.sender

# Update claimable amount
self.claimable_amount[Transaction.sender()] = pay.amount

@arc4.abimethod
def claim_bids(self) -> None:
original_amount = self.claimable_amount[Transaction.sender()]

amount = original_amount

# subtract previous bid if sender is previous bidder
if Transaction.sender() == self.previous_bidder:
amount -= self.previous_bid

CreateInnerTransaction.begin()
CreateInnerTransaction.set_type_enum(TransactionType.Payment)
CreateInnerTransaction.set_fee(UInt64(0)) # cover fee with outer txn
CreateInnerTransaction.set_receiver(Transaction.sender())
CreateInnerTransaction.set_asset_amount(amount)
CreateInnerTransaction.submit()

self.claimable_amount[Transaction.sender()] = original_amount - amount

@arc4.abimethod
def claim_asset(self, asset: Asset) -> None:
assert Global.latest_timestamp() > self.auction_end, "auction has not ended"

# Send ASA to previous bidder
CreateInnerTransaction.begin()
CreateInnerTransaction.set_type_enum(TransactionType.AssetTransfer)
CreateInnerTransaction.set_fee(UInt64(0)) # cover fee with outer txn
CreateInnerTransaction.set_asset_receiver(self.previous_bidder)
CreateInnerTransaction.set_xfer_asset(asset.asset_id)
CreateInnerTransaction.set_asset_amount(self.asa_amount)
CreateInnerTransaction.set_asset_close_to(self.previous_bidder)
CreateInnerTransaction.submit()

@subroutine
def delete_application(self) -> None:
CreateInnerTransaction.begin()
CreateInnerTransaction.set_type_enum(TransactionType.Payment)
CreateInnerTransaction.set_fee(UInt64(0)) # cover fee with outer txn
CreateInnerTransaction.set_receiver(Global.creator_address())
CreateInnerTransaction.set_close_remainder_to(Global.creator_address())
CreateInnerTransaction.submit()

def clear_state_program(self) -> bool:
return True
Loading

0 comments on commit 08d87c5

Please sign in to comment.