Skip to content

Commit

Permalink
chore(test): add integration test for buying a lease expired auction
Browse files Browse the repository at this point in the history
  • Loading branch information
dtfiedler committed Oct 23, 2024
1 parent dd5bf5a commit 232823d
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 5 deletions.
6 changes: 4 additions & 2 deletions src/arns.lua
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,10 @@ function arns.submitAuctionBid(name, bidAmount, bidder, timestamp, processId, ty
purchasePrice = finalBidAmount,
type = type,
}
local rewardForInitiator = math.floor(finalBidAmount * 0.5)
local rewardForProtocol = finalBidAmount - rewardForInitiator

-- if the initiator is the protocol, all funds go to the protocol
local rewardForInitiator = auction.initiator ~= ao.id and math.floor(finalBidAmount * 0.5) or 0
local rewardForProtocol = auction.initiator ~= ao.id and finalBidAmount - rewardForInitiator or finalBidAmount
-- reduce bidder balance by the final bid amount
balances.transfer(auction.initiator, bidder, rewardForInitiator)
balances.transfer(ao.id, bidder, rewardForProtocol)
Expand Down
4 changes: 2 additions & 2 deletions src/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2350,7 +2350,7 @@ addEventingHandler("auctionBid", utils.hasMatchingTag("Action", ActionMap.Auctio
local bidder = utils.formatAddress(msg.From)
local processId = utils.formatAddress(msg.Tags["Process-Id"])
local timestamp = tonumber(msg.Timestamp)
local type = msg.Tags.Type or "permabuy"
local type = msg.Tags["Purchase-Type"] or "permabuy"
local years = msg.Tags.Years and tonumber(msg.Tags.Years) or nil

-- assert name, bidder, processId are provided
Expand All @@ -2376,7 +2376,7 @@ addEventingHandler("auctionBid", utils.hasMatchingTag("Action", ActionMap.Auctio
"Years must be an integer between 1 and 5"
)
else
years = 1
years = years or 1
end
end
end
Expand Down
194 changes: 193 additions & 1 deletion tests/arns.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,199 @@ describe('ArNS', async () => {
assert.equal(balances[bidderAddress], 0);
});

// TODO: add a test to create a lease initiated dutch auction that accepts a bid with explicit parameters
// TODO: add a test to create a lease expiration intiaited auction and accept a bid
it('should create a lease expiration intiaited auction and accept a bid', async () => {
const { record: initialRecord, mem } = await runBuyRecord({
sender: STUB_ADDRESS,
processId: ''.padEnd(43, 'a'),
type: 'lease',
years: 1,
});

// tick the contract after the lease leaves its grace period
const futureTimestamp =
initialRecord.endTimestamp + 60 * 1000 * 60 * 24 * 14 + 1;
const tickResult = await handle(
{
Tags: [{ name: 'Action', value: 'Tick' }],
Timestamp: futureTimestamp,
},
mem,
);

// fetch the auction
const auctionResult = await handle(
{
Tags: [
{ name: 'Action', value: 'Auction-Info' },
{ name: 'Name', value: 'test-name' },
],
},
tickResult.Memory,
);
// assert no error tag
const auctionErrorTag = auctionResult.Messages?.[0]?.Tags?.find(
(tag) => tag.name === 'Error',
);

assert.equal(auctionErrorTag, undefined);
const auction = JSON.parse(auctionResult.Messages?.[0]?.Data);
assert.deepEqual(auction, {
name: 'test-name',
initiator: PROCESS_ID,
startTimestamp: futureTimestamp,
endTimestamp: futureTimestamp + 60 * 60 * 1000 * 24 * 14,
baseFee: 500000000,
demandFactor: 1,
settings: {
decayRate: 0.02037911 / (1000 * 60 * 60 * 24 * 14),
scalingExponent: 190,
durationMs: 1209600000,
startPriceMultiplier: 50,
},
});

// // TRANSFER FROM THE OWNER TO A NEW STUB ADDRESS
const bidYears = 3;
const expectedFloorPrice = Math.floor(
auction.baseFee + auction.baseFee * bidYears * 0.2,
);
const expectedStartPrice = Math.floor(
expectedFloorPrice * auction.settings.startPriceMultiplier,
);
const bidderAddress = 'auction-bidder-'.padEnd(43, '0');
const bidTimestamp = futureTimestamp + 60 * 60 * 1000 * 24 * 7; // 7 days into the auction
const expectedPurchasePrice = Math.floor(
expectedStartPrice *
(1 - auction.settings.decayRate * (bidTimestamp - futureTimestamp)) **
auction.settings.scalingExponent,
);
const transferResult = await handle(
{
From: PROCESS_OWNER,
Owner: PROCESS_OWNER,
Tags: [
{ name: 'Action', value: 'Transfer' },
{ name: 'Recipient', value: bidderAddress },
{ name: 'Quantity', value: `${expectedPurchasePrice}` },
{ name: 'Cast', value: true },
],
Timestamp: bidTimestamp - 1,
},
tickResult.Memory,
);

// assert no error in the transfer
const transferErrorTag = transferResult.Messages?.[0]?.Tags?.find(
(tag) => tag.name === 'Error',
);

assert.equal(transferErrorTag, undefined);
const processId = 'new-name-owner-'.padEnd(43, '1');
const submitBidResult = await handle(
{
From: bidderAddress,
Owner: bidderAddress,
Tags: [
{ name: 'Action', value: 'Auction-Bid' },
{ name: 'Name', value: 'test-name' },
{ name: 'Process-Id', value: processId },
{ name: 'Purchase-Type', value: 'lease' },
{ name: 'Years', value: bidYears },
],
Timestamp: bidTimestamp,
},
transferResult.Memory,
);

// assert no error tag
const submitBidErrorTag = submitBidResult.Messages[0].Tags.find(
(tag) => tag.name === 'Error',
);
assert.equal(submitBidErrorTag, undefined);

// should send three messages including a Buy-Record-Notice and a Debit-Notice
assert.equal(submitBidResult.Messages.length, 2);

// should send a buy record notice
const buyRecordNoticeTag = submitBidResult.Messages?.[0]?.Tags?.find(
(tag) => tag.name === 'Action' && tag.value === 'Buy-Record-Notice',
);

assert.ok(buyRecordNoticeTag);

// expect the target tag to be the bidder
assert.equal(submitBidResult.Messages?.[0]?.Target, bidderAddress);

const expectedRecord = {
processId,
purchasePrice: expectedPurchasePrice,
startTimestamp: bidTimestamp,
endTimestamp: bidTimestamp + 60 * 60 * 1000 * 24 * 365 * 3,
undernameLimit: 10,
type: 'lease',
};
// the protocol gets the entire bid amount
const expectedRewardForProtocol = expectedPurchasePrice;

// assert the data response contains the record
const buyRecordNoticeData = JSON.parse(submitBidResult.Messages?.[0]?.Data);
assert.deepEqual(buyRecordNoticeData, {
name: 'test-name',
...expectedRecord,
});

// should send a debit notice
const debitNoticeTag = submitBidResult.Messages?.[1]?.Tags?.find(
(tag) => tag.name === 'Action' && tag.value === 'Debit-Notice',
);
assert.ok(debitNoticeTag);

// expect the target to be to the protocol balance
assert.equal(submitBidResult.Messages?.[1]?.Target, PROCESS_ID);

// assert the data response contains the record
const debitNoticeData = JSON.parse(submitBidResult.Messages?.[1]?.Data);
assert.deepEqual(debitNoticeData, {
record: expectedRecord,
bidder: bidderAddress,
bidAmount: expectedPurchasePrice,
rewardForInitiator: 0,
rewardForProtocol: expectedRewardForProtocol,
name: 'test-name',
});

// should add the record to the registry
const recordResult = await handle(
{
Tags: [
{ name: 'Action', value: 'Record' },
{ name: 'Name', value: 'test-name' },
],
Timestamp: bidTimestamp,
},
submitBidResult.Memory,
);

const record = JSON.parse(recordResult.Messages?.[0]?.Data);
assert.deepEqual(record, expectedRecord);

// assert the balance of the initiator and the protocol where updated correctly
const balancesResult = await handle(
{
Tags: [{ name: 'Action', value: 'Balances' }],
},
submitBidResult.Memory,
);

const expectedProtocolBalance =
INITIAL_PROTOCOL_BALANCE +
initialRecord.purchasePrice +
expectedRewardForProtocol;
const balances = JSON.parse(balancesResult.Messages[0].Data);
assert.equal(balances[PROCESS_ID], expectedProtocolBalance);
assert.equal(balances[bidderAddress], 0);
});

it('should compute the prices of an auction at a specific interval', async () => {
// buy the name first
Expand Down

0 comments on commit 232823d

Please sign in to comment.