The Rust Contracts
The content on this page assumes that you already have Rust and Cargo installed in your machine
First, clone the repo:
git clone git@github.com:aufacicenta/pulsemarkets.git prompt-wars
Contract files structure:
.
├── market-factory
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── build.sh
│ └── src
│ ├── callbacks.rs
│ ├── consts.rs
│ ├── contract.rs
│ ├── lib.rs
│ ├── storage.rs
│ ├── tests.rs
│ └── views.rs
└── prompt-wars
├── Cargo.lock
├── Cargo.toml
├── README.md
├── build.sh
└── src
├── callbacks.rs
├── consts.rs
├── contract.rs
├── flags.rs
├── ft_receiver.rs
├── lib.rs
├── math.rs
├── modifiers.rs
├── outcome_token.rs
├── storage.rs
├── tests.rs
└── views.rs
You'll want to focus on the contract.rs
files most of the time, but storage.rs
is your starting point, for example prompt-wars/src/storage.rs
holds all the public structs that the game uses:
use near_sdk::{
borsh::{self, BorshDeserialize, BorshSerialize},
collections::{LookupMap, Vector},
near_bindgen,
serde::{Deserialize, Serialize},
AccountId, BorshStorageKey,
};
use shared::OutcomeId;
pub type Timestamp = i64;
pub type WrappedBalance = u128;
pub type OutcomeTokenResult = f32;
pub type ResolutionResult = OutcomeId;
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Clone)]
#[cfg_attr(not(target_arch = "wasm32"), derive(Debug, PartialEq))]
#[serde(crate = "near_sdk::serde")]
pub struct MarketData {
// The IPFS reference-image hash of the expected prompts
pub image_uri: String,
// Datetime nanos: the market is open
pub starts_at: Timestamp,
// Datetime nanos: the market is closed
pub ends_at: Timestamp,
}
#[near_bindgen]
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Market {
// Market metadata
pub market: MarketData,
// NEP141 token metadata
pub collateral_token: CollateralToken,
// MArket resolution metadata
pub resolution: Resolution,
// Market management account ids metadata
pub management: Management,
// Keeps track of Outcomes prices and balances
pub outcome_tokens: LookupMap<AccountId, OutcomeToken>,
// Keeps track of the outcome_ids that have bet on the market
pub players: Vector<AccountId>,
// Market fees metadata
pub fees: Fees,
}
#[derive(Serialize, Deserialize)]
pub enum SetPriceOptions {
Increase,
Decrease,
}
#[derive(BorshSerialize, BorshDeserialize, Serialize)]
pub struct OutcomeToken {
// the account id of the outcome_token
pub outcome_id: OutcomeId,
// the outcome value, in this case, the prompt submitted to the competition
pub prompt: String,
// the outcome value, in this case, the prompt submitted to the competition
pub output_img_uri: Option<String>,
// store the result from the image comparison: percentage_diff or pixel_difference
pub result: Option<OutcomeTokenResult>,
// total supply of this outcome_token
pub total_supply: WrappedBalance,
}
#[derive(BorshSerialize, BorshDeserialize, Deserialize, Serialize, Clone)]
pub struct CollateralToken {
pub id: AccountId,
pub balance: WrappedBalance,
pub decimals: u8,
pub fee_balance: WrappedBalance,
}
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Clone)]
pub struct Resolution {
// Time to free up the market
pub window: Timestamp,
// Time after the market ends and before the resolution window starts
pub reveal_window: Timestamp,
// When the market is resolved, set only by fn resolve
pub resolved_at: Option<Timestamp>,
// When the market is resolved, set only by fn resolve
pub result: Option<ResolutionResult>,
}
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Clone)]
pub struct Management {
// Gets sent fees when claiming window is open
pub dao_account_id: AccountId,
// Gets back the storage deposit upon self-destruction
pub market_creator_account_id: AccountId,
// Set at initialization, the market may be destroyed by the creator to claim the storage deposit
pub self_destruct_window: Timestamp,
// Set at initialization, determines when to close bets
pub buy_sell_threshold: f32,
}
#[derive(BorshSerialize, BorshDeserialize, Deserialize, Serialize, Clone)]
pub struct Fees {
// Price to charge when creating an outcome token
pub price: WrappedBalance,
// Decimal fee to charge upon a bet
pub fee_ratio: WrappedBalance,
// When fees got sent to the DAO
pub claimed_at: Option<Timestamp>,
}
#[derive(BorshStorageKey, BorshSerialize)]
pub enum StorageKeys {
OutcomeTokens,
StakingFees,
MarketCreatorFees,
}
#[derive(Serialize, Deserialize)]
pub struct CreateOutcomeTokenArgs {
// the outcome value, in this case, the prompt submitted to the competition
pub prompt: String,
}
#[derive(Serialize, Deserialize)]
pub enum Payload {
CreateOutcomeTokenArgs(CreateOutcomeTokenArgs),
}
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Clone)]
pub struct Ix {
pub address: [u8; 32],
}
Note that these structs, have their corresponding Typescript definitions:
app/src/providers/near/contracts/prompt-wars/prompt-wars.types.ts
type Timestamp = number;
type WrappedBalance = number;
export type AccountId = string;
export type OutcomeId = AccountId;
export type OutcomeTokenResult = number;
export type Prompt = {
value: string;
negative_prompt?: string;
};
export type ResolutionResult = AccountId;
export type MarketData = {
image_uri: string;
starts_at: Timestamp;
ends_at: Timestamp;
};
export type Fees = {
price: WrappedBalance;
fee_ratio: WrappedBalance;
claiming_window?: Timestamp;
};
export type Management = {
dao_account_id: AccountId;
self_destruct_window: Timestamp;
buy_sell_threshold: number;
market_creator_account_id?: AccountId;
};
export type CollateralToken = {
id: string;
balance: number;
decimals: number;
fee_balance: WrappedBalance;
};
export type Resolution = {
window: Timestamp;
reveal_window: Timestamp;
resolved_at?: Timestamp;
result?: string;
};
export type OutcomeToken = {
outcome_id: OutcomeId;
prompt: string;
total_supply: WrappedBalance;
result?: OutcomeTokenResult;
output_img_uri?: string;
};
export type PromptWarsMarketContractValues = {
market: MarketData;
resolution: Resolution;
fees: Fees;
management: Management;
collateralToken: CollateralToken;
outcomeIds: Array<OutcomeId>;
isResolved: boolean;
isOpen: boolean;
isOver: boolean;
isRevealWindowExpired: boolean;
isResolutionWindowExpired: boolean;
isExpiredUnresolved: boolean;
status: PromptWarsMarketContractStatus;
};
export enum PromptWarsMarketContractStatus {
LOADING = "Loading",
OPEN = "Open",
REVEALING = "Revealing",
RESOLVING = "Resolving",
RESOLVED = "Resolved",
UNRESOLVED = "Unresolved",
CLOSED = "Closed",
}
export type GetOutcomeTokenArgs = {
outcome_id: OutcomeId;
};
export type PromptWarsMarketContractMethods = {
get_market_data: () => Promise<MarketData>;
get_resolution_data: () => Promise<Resolution>;
get_fee_data: () => Promise<Fees>;
get_management_data: () => Promise<Management>;
get_collateral_token_metadata: () => Promise<CollateralToken>;
get_outcome_token: (args: GetOutcomeTokenArgs) => Promise<OutcomeToken>;
get_outcome_ids: () => Promise<Array<AccountId>>;
get_block_timestamp: () => Promise<Timestamp>;
resolved_at: () => Promise<Timestamp>;
balance_of: (outcome_id: OutcomeId) => Promise<WrappedBalance>;
get_amount_mintable: (amount: WrappedBalance) => Promise<Array<WrappedBalance>>;
get_amount_payable_unresolved: () => Promise<Array<WrappedBalance>>;
get_amount_payable_resolved: () => Promise<Array<WrappedBalance>>;
get_precision_decimals: () => Promise<WrappedBalance>;
// flags
is_resolved: () => Promise<boolean>;
is_open: () => Promise<boolean>;
is_over: () => Promise<boolean>;
is_reveal_window_expired: () => Promise<boolean>;
is_resolution_window_expired: () => Promise<boolean>;
is_self_destruct_window_expired: () => Promise<boolean>;
is_expired_unresolved: () => Promise<boolean>;
// mutators
sell: (args: { name: string; args: string }, gas?: number, amount?: string | null) => Promise<boolean>;
reveal: (
args: { outcome_id: OutcomeId; result: OutcomeTokenResult },
gas?: number,
amount?: string | null,
) => Promise<boolean>;
resolve: (args: { name: string; args: string }, gas?: number, amount?: string | null) => Promise<boolean>;
};
Testing Rust contracts
Both prompt-wars and market-factory have a tests.rs
file. If you make changes to any of these, you may check for errors by running:
cargo test -- --nocapture
This command should result in 100% passing tests:
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests promptwars
Last updated