Skip to content

Increase a bid ​

The exact contract function is:

solidity
increaseBid(uint256 submissionId, uint256 additionalAmount)

Only the original author may increase their submission while the auction is open. increaseBid accepts an additional CLAY amount at or above the required delta for that submission to take the lead. A matching total does not replace the current leader.

Before calling increaseBid:

  1. read the submission and confirm the connected account is its author;
  2. confirm that the auction remains open;
  3. read the current leader, leading bid, protocol minimum and submission bid from the same current block;
  4. derive the required delta using the contract rule;
  5. verify balance and allowance for that delta;
  6. approve only the bounded additional CLAY if required;
  7. refresh all critical state after approval; and
  8. simulate before requesting a signature.

The additional amount may exceed the required delta. The example uses the minimum required delta to limit the CLAY approval and escrowed amount.

Example: calculate and submit a bounded increase ​

ts
const increased = await increaseBid(42n);
ts
import {
  encodeFunctionData,
  getAddress,
  parseEventLogs,
  type Hash,
} from "viem";

import { tabletsAbi } from "@tablets/abi";

import {
  readCurrentAuction,
  type CurrentAuctionQuote,
} from "./read-current-auction.js";
import {
  MAX_UINT256,
  TABLETS_ADDRESS,
  assertPin,
  ensureBoundedAllowance,
  getActiveAccount,
  invariant,
  only,
  publicClient,
  readTokenFunds,
  waitForExactTransaction,
  walletClient,
} from "./tablets-client.js";

export type IncreasedBid = {
  readonly transactionHash: Hash;
  readonly approvalHash: Hash | null;
  readonly blockNumber: bigint;
  readonly auctionId: bigint;
  readonly submissionId: bigint;
  readonly previousBid: bigint;
  readonly delta: bigint;
  readonly newBid: bigint;
};

function sameIncreaseFacts(
  before: CurrentAuctionQuote,
  after: CurrentAuctionQuote,
): boolean {
  return (
    before.auctionId === after.auctionId &&
    before.cutoff === after.cutoff &&
    before.minimumBid === after.minimumBid &&
    before.leaderSubmissionId === after.leaderSubmissionId &&
    before.leaderBid === after.leaderBid &&
    before.accountSubmissionId === after.accountSubmissionId &&
    before.accountBid === after.accountBid &&
    before.requiredIncreaseDelta === after.requiredIncreaseDelta
  );
}

export async function increaseBid(submissionId: bigint): Promise<IncreasedBid> {
  invariant(submissionId > 0n, "submissionId must be positive");
  const account = await getActiveAccount();
  const quote = await readCurrentAuction(account);
  invariant(
    quote.accountSubmissionId === submissionId &&
      quote.requiredIncreaseDelta !== null,
    "The connected account is not the author of this current-auction entry",
  );

  const requiredDelta = quote.requiredIncreaseDelta;
  invariant(
    quote.accountBid <= MAX_UINT256 - requiredDelta,
    "Resulting bid exceeds uint256",
  );
  const approvalHash = await ensureBoundedAllowance(account, requiredDelta);

  const refreshed = await readCurrentAuction(account);
  invariant(
    sameIncreaseFacts(quote, refreshed),
    "Leader, minimum, author bid, or cutoff changed; show a new quote",
  );
  const funds = await readTokenFunds(account, requiredDelta, refreshed.block);
  invariant(
    funds.allowance >= requiredDelta,
    "Bounded allowance is no longer sufficient",
  );

  const { request } = await publicClient.simulateContract({
    address: TABLETS_ADDRESS,
    abi: tabletsAbi,
    functionName: "increaseBid",
    args: [submissionId, requiredDelta],
    account,
    blockNumber: refreshed.block.number,
  });
  await assertPin(refreshed.block);

  const input = encodeFunctionData({
    abi: tabletsAbi,
    functionName: "increaseBid",
    args: [submissionId, requiredDelta],
  });
  const transactionHash = await walletClient.writeContract(request);
  const receipt = await waitForExactTransaction(transactionHash, {
    account,
    to: TABLETS_ADDRESS,
    input,
  });
  const event = only(
    parseEventLogs({
      abi: tabletsAbi,
      eventName: "BidIncreased",
      logs: receipt.logs.filter(
        (log) => getAddress(log.address) === TABLETS_ADDRESS,
      ),
      strict: true,
    }),
    "BidIncreased event",
  );
  invariant(
    event.args.auctionId === refreshed.auctionId &&
      event.args.submissionId === submissionId &&
      getAddress(event.args.submitter) === account &&
      event.args.previousBid === refreshed.accountBid &&
      event.args.delta === requiredDelta &&
      event.args.newBid === refreshed.accountBid + requiredDelta,
    "BidIncreased event mismatch",
  );

  const stored = await publicClient.readContract({
    address: TABLETS_ADDRESS,
    abi: tabletsAbi,
    functionName: "getSubmission",
    args: [submissionId],
    blockNumber: receipt.blockNumber,
  });
  invariant(
    getAddress(stored.submitter) === account &&
      stored.bidAmount === event.args.newBid,
    "Receipt-pinned bid state mismatch",
  );

  return {
    transactionHash,
    approvalHash,
    blockNumber: receipt.blockNumber,
    auctionId: event.args.auctionId,
    submissionId,
    previousBid: event.args.previousBid,
    delta: event.args.delta,
    newBid: event.args.newBid,
  };
}

If another transaction changes the leader or required delta, read the latest state and construct a new call. Do not increase the amount automatically.