Arbitrum Stylus logo

Stylus by Example

English Auction

An Arbitrum Stylus version implementation of Solidity English Auction.

Auction

  1. Seller of NFT deploys this contract.
  2. Auction lasts for 7 days.
  3. Participants can bid by depositing ETH greater than the current highest bidder.
  4. All bidders can withdraw their bid if it is not the current highest bid.

After the auction

  1. Highest bidder becomes the new owner of NFT.
  2. The seller receives the highest bid of ETH.

Here is the interface for English Auction.

1interface IEnglishAuction {
2    function nft() external view returns (address);
3
4    function nftId() external view returns (uint256);
5
6    function seller() external view returns (address);
7
8    function endAt() external view returns (uint256);
9
10    function started() external view returns (bool);
11
12    function ended() external view returns (bool);
13
14    function highestBidder() external view returns (address);
15
16    function highestBid() external view returns (uint256);
17
18    function bids(address bidder) external view returns (uint256);
19
20    function initialize(address nft, uint256 nft_id, uint256 starting_bid) external;
21
22    function start() external;
23
24    function bid() external payable;
25
26    function withdraw() external;
27
28    function end() external;
29
30    error AlreadyInitialized();
31
32    error AlreadyStarted();
33
34    error NotSeller();
35
36    error AuctionEnded();
37
38    error BidTooLow();
39
40    error NotStarted();
41
42    error NotEnded();
43}
1interface IEnglishAuction {
2    function nft() external view returns (address);
3
4    function nftId() external view returns (uint256);
5
6    function seller() external view returns (address);
7
8    function endAt() external view returns (uint256);
9
10    function started() external view returns (bool);
11
12    function ended() external view returns (bool);
13
14    function highestBidder() external view returns (address);
15
16    function highestBid() external view returns (uint256);
17
18    function bids(address bidder) external view returns (uint256);
19
20    function initialize(address nft, uint256 nft_id, uint256 starting_bid) external;
21
22    function start() external;
23
24    function bid() external payable;
25
26    function withdraw() external;
27
28    function end() external;
29
30    error AlreadyInitialized();
31
32    error AlreadyStarted();
33
34    error NotSeller();
35
36    error AuctionEnded();
37
38    error BidTooLow();
39
40    error NotStarted();
41
42    error NotEnded();
43}

Example implementation of a English Auction contract written in Rust.

src/lib.rs

1// Allow `cargo stylus export-abi` to generate a main function.
2#![cfg_attr(not(feature = "export-abi"), no_main)]
3extern crate alloc;
4
5use std::borrow::BorrowMut;
6
7/// Import items from the SDK. The prelude contains common traits and macros.
8use stylus_sdk::{alloy_primitives::{Address, U256}, block, call::{transfer_eth, Call}, contract, evm, msg, prelude::*};
9use alloy_sol_types::sol;
10
11// Import the IERC721 interface.
12sol_interface! {
13    interface IERC721 {
14        // Required methods.
15        function safeTransferFrom(address from, address to, uint256 token_id) external;
16        function transferFrom(address, address, uint256) external;
17    }
18}
19
20// Define the events and errors for the contract.
21sol!{
22    // Define the events for the contract.
23    event Start(); // Start the auction.
24    event Bid(address indexed sender, uint256 amount); // Bid on the auction.
25    event Withdraw(address indexed bidder, uint256 amount); // Withdraw a bid.
26    event End(address winner, uint256 amount); // End the auction.
27
28    // Define the errors for the contract.
29    error AlreadyInitialized(); // The contract has already been initialized.
30    error AlreadyStarted(); // The auction has already started.
31    error NotSeller(); // The sender is not the seller.
32    error AuctionEnded(); // The auction has ended.
33    error BidTooLow(); // The bid is too low.
34    error NotStarted(); // The auction has not started.
35    error NotEnded(); // The auction has not ended.
36}
37
38
39#[derive(SolidityError)]
40pub enum EnglishAuctionError {
41    // Define the errors for the contract.
42    AlreadyInitialized(AlreadyInitialized),
43    AlreadyStarted(AlreadyStarted),
44    NotSeller(NotSeller),
45    AuctionEnded(AuctionEnded),
46    BidTooLow(BidTooLow),
47    NotStarted(NotStarted),
48    NotEnded(NotEnded),
49}
50
51// Define some persistent storage using the Solidity ABI.
52// `Counter` will be the entrypoint.
53sol_storage! {
54    #[entrypoint]
55    pub struct EnglishAuction {
56        address nft_address; // The address of the NFT contract.
57        uint256 nft_id; // The ID of the NFT.
58
59        address seller; // The address of the seller.
60        uint256 end_at; // The end time of the auction.
61        bool started; // The auction has started or not.
62        bool ended; // The auction has ended or not.
63
64        address highest_bidder; // The address of the highest bidder.
65        uint256 highest_bid; // The highest bid.
66        mapping(address => uint256) bids; // The bids of the bidders.
67    }
68}
69
70/// Declare that `Counter` is a contract with the following external methods.
71#[public]
72impl EnglishAuction {
73    pub const ONE_DAY: u64 = 86400; // 1 day = 24 hours * 60 minutes * 60 seconds = 86400 seconds.
74    
75    // Get nft address
76    pub fn nft(&self) -> Result<Address, EnglishAuctionError> {
77        Ok(self.nft_address.get())
78    }
79
80    // Get nft id
81    pub fn nft_id(&self) -> Result<U256, EnglishAuctionError> {
82        Ok(self.nft_id.get())
83    }
84    // Get seller address
85    pub fn seller(&self) -> Result<Address, EnglishAuctionError> {
86        Ok(self.seller.get())
87    }
88
89    // Get end time
90    pub fn end_at(&self) -> Result<U256, EnglishAuctionError> {
91        Ok(self.end_at.get())
92    }
93
94    // Get started status
95    pub fn started(&self) -> Result<bool, EnglishAuctionError> {
96        Ok(self.started.get())
97    }
98
99    // Get ended status
100    pub fn ended(&self) -> Result<bool, EnglishAuctionError> {
101        Ok(self.ended.get())
102    }
103
104    // Get highest bidder address
105    pub fn highest_bidder(&self) -> Result<Address, EnglishAuctionError> {
106        Ok(self.highest_bidder.get())
107    }
108
109    // Get highest bid amount
110    pub fn highest_bid(&self) -> Result<U256, EnglishAuctionError> {
111        Ok(self.highest_bid.get())
112    }
113
114    // Get bid amount of a bidder
115    pub fn bids(&self, bidder: Address) -> Result<U256, EnglishAuctionError> {
116        Ok(self.bids.getter(bidder).get())
117    }
118
119    // Initialize program
120    pub fn initialize(&mut self, nft: Address, nft_id: U256, starting_bid: U256) -> Result<(), EnglishAuctionError> {
121        // Check if the contract has already been initialized.
122        if self.seller.get() != Address::default() {
123            // Return an error if the contract has already been initialized.
124            return Err(EnglishAuctionError::AlreadyInitialized(AlreadyInitialized{}));
125        }
126        
127        // Initialize the contract with the NFT address, the NFT ID, the seller, and the starting bid.
128        self.nft_address.set(nft);
129        self.nft_id.set(nft_id);
130        self.seller.set(msg::sender());
131        self.highest_bid.set(starting_bid);
132        Ok(())
133    }
134
135    pub fn start(&mut self) -> Result<(), EnglishAuctionError> {
136        // Check if the auction has already started.
137        if self.started.get() {
138            return Err(EnglishAuctionError::AlreadyStarted(AlreadyStarted{}));
139        }
140        
141        // Check if the sender is the seller.
142        if self.seller.get() != msg::sender() {
143            // Return an error if the sender is the seller.
144            return Err(EnglishAuctionError::NotSeller(NotSeller{}));
145        }
146        
147        // Create a new instance of the IERC721 interface.
148        let nft = IERC721::new(*self.nft_address);
149        // Get the NFT ID.
150        let nft_id = self.nft_id.get();
151
152        // Transfer the NFT to the contract.
153        let config = Call::new_in(self);
154        let result = nft.transfer_from(config, msg::sender(), contract::address(), nft_id);
155        
156        match result {
157            // If the transfer is successful, start the auction.
158            Ok(_) => {
159                self.started.set(true);
160                // Set the end time of the auction to 7 days from now.
161                self.end_at.set(U256::from(block::timestamp() + 7 * Self::ONE_DAY));
162                // Log the start event.
163                evm::log(Start {});
164                Ok(())
165            },
166            // If the transfer fails, return an error.
167            Err(_) => {
168                return Err(EnglishAuctionError::NotSeller(NotSeller{}));
169            }
170            
171        }
172    }
173
174    // The bid method allows bidders to place a bid on the auction.
175    #[payable]
176    pub fn bid(&mut self) -> Result<(), EnglishAuctionError> {
177        // Check if the auction has started.
178        if !self.started.get() {
179            // Return an error if the auction has not started.
180            return Err(EnglishAuctionError::NotSeller(NotSeller{}));
181        }
182        
183        // Check if the auction has ended.
184        if U256::from(block::timestamp()) >= self.end_at.get() {
185            // Return an error if the auction has ended.
186            return Err(EnglishAuctionError::AuctionEnded(AuctionEnded{}));
187        }
188        
189        // Check if the bid amount is higher than the current highest bid.
190        if msg::value() <= self.highest_bid.get() {
191            // Return an error if the bid amount is too low.
192            return Err(EnglishAuctionError::BidTooLow(BidTooLow{}));
193        }
194        
195        // Refund the previous highest bidder. (But will not transfer back at this call, needs bidders to call withdraw() to get back the fund.
196        if self.highest_bidder.get() != Address::default() {
197            let mut bid = self.bids.setter(self.highest_bidder.get());
198            let current_bid = bid.get();
199            bid.set(current_bid + self.highest_bid.get());
200        }
201        
202        // Update the highest bidder and the highest bid.
203        self.highest_bidder.set(msg::sender());
204        self.highest_bid.set(msg::value());
205
206        // Update the bid of the current bidder.
207        evm::log(Bid {
208            sender: msg::sender(),
209            amount: msg::value(),
210        });
211        Ok(())
212    }
213
214    // The withdraw method allows bidders to withdraw their bid.
215    pub fn withdraw(&mut self) -> Result<(), EnglishAuctionError> {
216        // Get the current bid of the bidder.
217        let mut current_bid = self.bids.setter(msg::sender());
218        let bal = current_bid.get();
219        // Set the record of this bidder to 0 and transfer back tokens.
220        current_bid.set(U256::from(0));
221        let _ = transfer_eth(msg::sender(), bal);
222
223        // Log the withdraw event.
224        evm::log(Withdraw {
225            bidder: msg::sender(),
226            amount: bal,
227        });
228        Ok(())
229    }
230
231    // The end method allows the seller to end the auction.
232    pub fn end<S: TopLevelStorage + BorrowMut<Self>>(storage: &mut S) -> Result<(), EnglishAuctionError> {
233        // Check if the auction has started.
234        if !storage.borrow_mut().started.get() {
235            // Return an error if the auction has not started.
236            return Err(EnglishAuctionError::NotStarted(NotStarted{}));
237        }
238        
239        // Check if the auction has ended.
240        if U256::from(block::timestamp()) < storage.borrow_mut().end_at.get() {
241            // Return an error if the auction has not ended.
242            return Err(EnglishAuctionError::NotEnded(NotEnded{}));
243        }
244        
245        // Check if the auction has already ended.
246        if storage.borrow_mut().ended.get() {
247            // Return an error if the auction has already ended.
248            return Err(EnglishAuctionError::AuctionEnded(AuctionEnded{}));
249        }
250        
251        // End the auction and transfer the NFT and the highest bid to the winner.
252        storage.borrow_mut().ended.set(true);
253        let nft_contract_address = *storage.borrow_mut().nft_address;
254        let seller_address = storage.borrow_mut().seller.get();
255        let highest_bid = storage.borrow_mut().highest_bid.get();
256        let highest_bidder = storage.borrow_mut().highest_bidder.get();
257        let nft_id = storage.borrow_mut().nft_id.get();
258        let config = Call::new_in(storage.borrow_mut());
259        
260        let nft = IERC721::new(nft_contract_address);
261        
262        // Check if there is highest bidder.
263        if highest_bidder != Address::default() {
264            // If there is a highest bidder, transfer the NFT to the highest bidder.
265            let _ = nft.safe_transfer_from(config, contract::address(), highest_bidder, nft_id);
266            // Transfer the highest bid to the seller.
267            let _ = transfer_eth(seller_address, highest_bid);
268        } else {
269            // If there is no highest bidder, transfer the NFT back to the seller.
270            let _ = nft.safe_transfer_from(config, contract::address(), seller_address, nft_id);
271        }
272
273        // Log the end event.
274        evm::log(End {
275            winner: highest_bidder,
276            amount: highest_bid,
277        });
278        Ok(())
279    }
280}
1// Allow `cargo stylus export-abi` to generate a main function.
2#![cfg_attr(not(feature = "export-abi"), no_main)]
3extern crate alloc;
4
5use std::borrow::BorrowMut;
6
7/// Import items from the SDK. The prelude contains common traits and macros.
8use stylus_sdk::{alloy_primitives::{Address, U256}, block, call::{transfer_eth, Call}, contract, evm, msg, prelude::*};
9use alloy_sol_types::sol;
10
11// Import the IERC721 interface.
12sol_interface! {
13    interface IERC721 {
14        // Required methods.
15        function safeTransferFrom(address from, address to, uint256 token_id) external;
16        function transferFrom(address, address, uint256) external;
17    }
18}
19
20// Define the events and errors for the contract.
21sol!{
22    // Define the events for the contract.
23    event Start(); // Start the auction.
24    event Bid(address indexed sender, uint256 amount); // Bid on the auction.
25    event Withdraw(address indexed bidder, uint256 amount); // Withdraw a bid.
26    event End(address winner, uint256 amount); // End the auction.
27
28    // Define the errors for the contract.
29    error AlreadyInitialized(); // The contract has already been initialized.
30    error AlreadyStarted(); // The auction has already started.
31    error NotSeller(); // The sender is not the seller.
32    error AuctionEnded(); // The auction has ended.
33    error BidTooLow(); // The bid is too low.
34    error NotStarted(); // The auction has not started.
35    error NotEnded(); // The auction has not ended.
36}
37
38
39#[derive(SolidityError)]
40pub enum EnglishAuctionError {
41    // Define the errors for the contract.
42    AlreadyInitialized(AlreadyInitialized),
43    AlreadyStarted(AlreadyStarted),
44    NotSeller(NotSeller),
45    AuctionEnded(AuctionEnded),
46    BidTooLow(BidTooLow),
47    NotStarted(NotStarted),
48    NotEnded(NotEnded),
49}
50
51// Define some persistent storage using the Solidity ABI.
52// `Counter` will be the entrypoint.
53sol_storage! {
54    #[entrypoint]
55    pub struct EnglishAuction {
56        address nft_address; // The address of the NFT contract.
57        uint256 nft_id; // The ID of the NFT.
58
59        address seller; // The address of the seller.
60        uint256 end_at; // The end time of the auction.
61        bool started; // The auction has started or not.
62        bool ended; // The auction has ended or not.
63
64        address highest_bidder; // The address of the highest bidder.
65        uint256 highest_bid; // The highest bid.
66        mapping(address => uint256) bids; // The bids of the bidders.
67    }
68}
69
70/// Declare that `Counter` is a contract with the following external methods.
71#[public]
72impl EnglishAuction {
73    pub const ONE_DAY: u64 = 86400; // 1 day = 24 hours * 60 minutes * 60 seconds = 86400 seconds.
74    
75    // Get nft address
76    pub fn nft(&self) -> Result<Address, EnglishAuctionError> {
77        Ok(self.nft_address.get())
78    }
79
80    // Get nft id
81    pub fn nft_id(&self) -> Result<U256, EnglishAuctionError> {
82        Ok(self.nft_id.get())
83    }
84    // Get seller address
85    pub fn seller(&self) -> Result<Address, EnglishAuctionError> {
86        Ok(self.seller.get())
87    }
88
89    // Get end time
90    pub fn end_at(&self) -> Result<U256, EnglishAuctionError> {
91        Ok(self.end_at.get())
92    }
93
94    // Get started status
95    pub fn started(&self) -> Result<bool, EnglishAuctionError> {
96        Ok(self.started.get())
97    }
98
99    // Get ended status
100    pub fn ended(&self) -> Result<bool, EnglishAuctionError> {
101        Ok(self.ended.get())
102    }
103
104    // Get highest bidder address
105    pub fn highest_bidder(&self) -> Result<Address, EnglishAuctionError> {
106        Ok(self.highest_bidder.get())
107    }
108
109    // Get highest bid amount
110    pub fn highest_bid(&self) -> Result<U256, EnglishAuctionError> {
111        Ok(self.highest_bid.get())
112    }
113
114    // Get bid amount of a bidder
115    pub fn bids(&self, bidder: Address) -> Result<U256, EnglishAuctionError> {
116        Ok(self.bids.getter(bidder).get())
117    }
118
119    // Initialize program
120    pub fn initialize(&mut self, nft: Address, nft_id: U256, starting_bid: U256) -> Result<(), EnglishAuctionError> {
121        // Check if the contract has already been initialized.
122        if self.seller.get() != Address::default() {
123            // Return an error if the contract has already been initialized.
124            return Err(EnglishAuctionError::AlreadyInitialized(AlreadyInitialized{}));
125        }
126        
127        // Initialize the contract with the NFT address, the NFT ID, the seller, and the starting bid.
128        self.nft_address.set(nft);
129        self.nft_id.set(nft_id);
130        self.seller.set(msg::sender());
131        self.highest_bid.set(starting_bid);
132        Ok(())
133    }
134
135    pub fn start(&mut self) -> Result<(), EnglishAuctionError> {
136        // Check if the auction has already started.
137        if self.started.get() {
138            return Err(EnglishAuctionError::AlreadyStarted(AlreadyStarted{}));
139        }
140        
141        // Check if the sender is the seller.
142        if self.seller.get() != msg::sender() {
143            // Return an error if the sender is the seller.
144            return Err(EnglishAuctionError::NotSeller(NotSeller{}));
145        }
146        
147        // Create a new instance of the IERC721 interface.
148        let nft = IERC721::new(*self.nft_address);
149        // Get the NFT ID.
150        let nft_id = self.nft_id.get();
151
152        // Transfer the NFT to the contract.
153        let config = Call::new_in(self);
154        let result = nft.transfer_from(config, msg::sender(), contract::address(), nft_id);
155        
156        match result {
157            // If the transfer is successful, start the auction.
158            Ok(_) => {
159                self.started.set(true);
160                // Set the end time of the auction to 7 days from now.
161                self.end_at.set(U256::from(block::timestamp() + 7 * Self::ONE_DAY));
162                // Log the start event.
163                evm::log(Start {});
164                Ok(())
165            },
166            // If the transfer fails, return an error.
167            Err(_) => {
168                return Err(EnglishAuctionError::NotSeller(NotSeller{}));
169            }
170            
171        }
172    }
173
174    // The bid method allows bidders to place a bid on the auction.
175    #[payable]
176    pub fn bid(&mut self) -> Result<(), EnglishAuctionError> {
177        // Check if the auction has started.
178        if !self.started.get() {
179            // Return an error if the auction has not started.
180            return Err(EnglishAuctionError::NotSeller(NotSeller{}));
181        }
182        
183        // Check if the auction has ended.
184        if U256::from(block::timestamp()) >= self.end_at.get() {
185            // Return an error if the auction has ended.
186            return Err(EnglishAuctionError::AuctionEnded(AuctionEnded{}));
187        }
188        
189        // Check if the bid amount is higher than the current highest bid.
190        if msg::value() <= self.highest_bid.get() {
191            // Return an error if the bid amount is too low.
192            return Err(EnglishAuctionError::BidTooLow(BidTooLow{}));
193        }
194        
195        // Refund the previous highest bidder. (But will not transfer back at this call, needs bidders to call withdraw() to get back the fund.
196        if self.highest_bidder.get() != Address::default() {
197            let mut bid = self.bids.setter(self.highest_bidder.get());
198            let current_bid = bid.get();
199            bid.set(current_bid + self.highest_bid.get());
200        }
201        
202        // Update the highest bidder and the highest bid.
203        self.highest_bidder.set(msg::sender());
204        self.highest_bid.set(msg::value());
205
206        // Update the bid of the current bidder.
207        evm::log(Bid {
208            sender: msg::sender(),
209            amount: msg::value(),
210        });
211        Ok(())
212    }
213
214    // The withdraw method allows bidders to withdraw their bid.
215    pub fn withdraw(&mut self) -> Result<(), EnglishAuctionError> {
216        // Get the current bid of the bidder.
217        let mut current_bid = self.bids.setter(msg::sender());
218        let bal = current_bid.get();
219        // Set the record of this bidder to 0 and transfer back tokens.
220        current_bid.set(U256::from(0));
221        let _ = transfer_eth(msg::sender(), bal);
222
223        // Log the withdraw event.
224        evm::log(Withdraw {
225            bidder: msg::sender(),
226            amount: bal,
227        });
228        Ok(())
229    }
230
231    // The end method allows the seller to end the auction.
232    pub fn end<S: TopLevelStorage + BorrowMut<Self>>(storage: &mut S) -> Result<(), EnglishAuctionError> {
233        // Check if the auction has started.
234        if !storage.borrow_mut().started.get() {
235            // Return an error if the auction has not started.
236            return Err(EnglishAuctionError::NotStarted(NotStarted{}));
237        }
238        
239        // Check if the auction has ended.
240        if U256::from(block::timestamp()) < storage.borrow_mut().end_at.get() {
241            // Return an error if the auction has not ended.
242            return Err(EnglishAuctionError::NotEnded(NotEnded{}));
243        }
244        
245        // Check if the auction has already ended.
246        if storage.borrow_mut().ended.get() {
247            // Return an error if the auction has already ended.
248            return Err(EnglishAuctionError::AuctionEnded(AuctionEnded{}));
249        }
250        
251        // End the auction and transfer the NFT and the highest bid to the winner.
252        storage.borrow_mut().ended.set(true);
253        let nft_contract_address = *storage.borrow_mut().nft_address;
254        let seller_address = storage.borrow_mut().seller.get();
255        let highest_bid = storage.borrow_mut().highest_bid.get();
256        let highest_bidder = storage.borrow_mut().highest_bidder.get();
257        let nft_id = storage.borrow_mut().nft_id.get();
258        let config = Call::new_in(storage.borrow_mut());
259        
260        let nft = IERC721::new(nft_contract_address);
261        
262        // Check if there is highest bidder.
263        if highest_bidder != Address::default() {
264            // If there is a highest bidder, transfer the NFT to the highest bidder.
265            let _ = nft.safe_transfer_from(config, contract::address(), highest_bidder, nft_id);
266            // Transfer the highest bid to the seller.
267            let _ = transfer_eth(seller_address, highest_bid);
268        } else {
269            // If there is no highest bidder, transfer the NFT back to the seller.
270            let _ = nft.safe_transfer_from(config, contract::address(), seller_address, nft_id);
271        }
272
273        // Log the end event.
274        evm::log(End {
275            winner: highest_bidder,
276            amount: highest_bid,
277        });
278        Ok(())
279    }
280}

Cargo.toml

1[package]
2name = "stylus-auction-example"
3version = "0.1.7"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
7
8[dependencies]
9alloy-primitives = "=0.7.6"
10alloy-sol-types = "=0.7.6"
11mini-alloc = "0.4.2"
12stylus-sdk = "0.6.0"
13hex = "0.4.3"
14
15[dev-dependencies]
16tokio = { version = "1.12.0", features = ["full"] }
17ethers = "2.0"
18eyre = "0.6.8"
19
20[features]
21export-abi = ["stylus-sdk/export-abi"]
22
23[lib]
24crate-type = ["lib", "cdylib"]
25
26[profile.release]
27codegen-units = 1
28strip = true
29lto = true
30panic = "abort"
31opt-level = "s"
1[package]
2name = "stylus-auction-example"
3version = "0.1.7"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
7
8[dependencies]
9alloy-primitives = "=0.7.6"
10alloy-sol-types = "=0.7.6"
11mini-alloc = "0.4.2"
12stylus-sdk = "0.6.0"
13hex = "0.4.3"
14
15[dev-dependencies]
16tokio = { version = "1.12.0", features = ["full"] }
17ethers = "2.0"
18eyre = "0.6.8"
19
20[features]
21export-abi = ["stylus-sdk/export-abi"]
22
23[lib]
24crate-type = ["lib", "cdylib"]
25
26[profile.release]
27codegen-units = 1
28strip = true
29lto = true
30panic = "abort"
31opt-level = "s"