Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ path = "src/main.rs"

[dependencies]
polymarket-client-sdk = { version = "0.4", features = ["gamma", "data", "bridge", "clob", "ctf"] }
alloy = { version = "1.6.3", default-features = false, features = ["providers", "sol-types", "contract", "reqwest", "reqwest-rustls-tls", "signer-local", "signers"] }
alloy = { version = "1.6.3", default-features = false, features = ["providers", "rpc-types", "sol-types", "contract", "reqwest", "reqwest-rustls-tls", "signer-local", "signers"] }
clap = { version = "4", features = ["derive"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
serde_json = "1"
Expand Down
62 changes: 33 additions & 29 deletions src/commands/approve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use alloy::primitives::U256;
use alloy::sol;
use alloy::sol_types::SolCall;
use anyhow::{Context, Result};
use clap::{Args, Subcommand};
use polymarket_client_sdk::types::{Address, address};
Expand All @@ -12,6 +13,8 @@ use crate::auth;
use crate::output::OutputFormat;
use crate::output::approve::{ApprovalStatus, print_approval_status, print_tx_result};

use super::proxy;

/// Polygon USDC (same address as `USDC_ADDRESS_STR`; `address!` requires a literal).
const USDC_ADDRESS: Address = address!("0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174");

Expand Down Expand Up @@ -81,20 +84,26 @@ pub async fn execute(
args: ApproveArgs,
output: OutputFormat,
private_key: Option<&str>,
signature_type: Option<&str>,
) -> Result<()> {
match args.command {
ApproveCommand::Check { address } => check(address, private_key, output).await,
ApproveCommand::Set => set(private_key, output).await,
ApproveCommand::Check { address } => {
check(address, private_key, signature_type, output).await
}
ApproveCommand::Set => set(private_key, signature_type, output).await,
}
}

async fn check(
address_arg: Option<Address>,
private_key: Option<&str>,
signature_type: Option<&str>,
output: OutputFormat,
) -> Result<()> {
let owner: Address = if let Some(addr) = address_arg {
addr
} else if proxy::is_proxy_mode(signature_type)? {
proxy::derive_proxy_address(private_key)?
} else {
let signer = auth::resolve_signer(private_key)?;
polymarket_client_sdk::auth::Signer::address(&signer)
Expand Down Expand Up @@ -135,13 +144,13 @@ async fn check(
print_approval_status(&statuses, &output)
}

async fn set(private_key: Option<&str>, output: OutputFormat) -> Result<()> {
let provider = auth::create_provider(private_key).await?;
async fn set(
private_key: Option<&str>,
signature_type: Option<&str>,
output: OutputFormat,
) -> Result<()> {
let use_proxy = proxy::is_proxy_mode(signature_type)?;
let config = contract_config(POLYGON, false).context("No contract config for Polygon")?;

let usdc = IERC20::new(USDC_ADDRESS, provider.clone());
let ctf = IERC1155::new(config.conditional_tokens, provider.clone());

let targets = approval_targets()?;
let total = targets.len() * 2;

Expand All @@ -155,17 +164,14 @@ async fn set(private_key: Option<&str>, output: OutputFormat) -> Result<()> {
for target in &targets {
step += 1;
let label = format!("USDC \u{2192} {}", target.name);
let tx_hash = usdc
.approve(target.address, U256::MAX)
.send()
.await
.context(format!("Failed to send USDC approval for {}", target.name))?
.watch()
let calldata = IERC20::approveCall {
spender: target.address,
value: U256::MAX,
}
.abi_encode();
let (tx_hash, _) = proxy::send_call(private_key, use_proxy, USDC_ADDRESS, calldata)
.await
.context(format!(
"Failed to confirm USDC approval for {}",
target.name
))?;
.context(format!("Failed USDC approval for {}", target.name))?;

match output {
OutputFormat::Table => print_tx_result(step, total, &label, tx_hash),
Expand All @@ -179,17 +185,15 @@ async fn set(private_key: Option<&str>, output: OutputFormat) -> Result<()> {

step += 1;
let label = format!("CTF \u{2192} {}", target.name);
let tx_hash = ctf
.setApprovalForAll(target.address, true)
.send()
.await
.context(format!("Failed to send CTF approval for {}", target.name))?
.watch()
.await
.context(format!(
"Failed to confirm CTF approval for {}",
target.name
))?;
let calldata = IERC1155::setApprovalForAllCall {
operator: target.address,
approved: true,
}
.abi_encode();
let (tx_hash, _) =
proxy::send_call(private_key, use_proxy, config.conditional_tokens, calldata)
.await
.context(format!("Failed CTF approval for {}", target.name))?;

match output {
OutputFormat::Table => print_tx_result(step, total, &label, tx_hash),
Expand Down
180 changes: 111 additions & 69 deletions src/commands/ctf.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use alloy::primitives::U256;
use alloy::sol;
use alloy::sol_types::SolCall;
use anyhow::{Context, Result};
use clap::{Args, Subcommand};
use polymarket_client_sdk::ctf::types::{
CollectionIdRequest, ConditionIdRequest, MergePositionsRequest, PositionIdRequest,
RedeemNegRiskRequest, RedeemPositionsRequest, SplitPositionRequest,
CollectionIdRequest, ConditionIdRequest, PositionIdRequest,
};
use polymarket_client_sdk::types::{Address, B256};
use polymarket_client_sdk::{POLYGON, ctf};
Expand All @@ -13,8 +14,43 @@ use crate::auth;
use crate::output::OutputFormat;
use crate::output::ctf as ctf_output;

use super::proxy;
use super::{USDC_ADDRESS_STR, USDC_DECIMALS};

sol! {
interface IConditionalTokens {
function splitPosition(
address collateralToken,
bytes32 parentCollectionId,
bytes32 conditionId,
uint256[] calldata partition,
uint256 amount
) external;

function mergePositions(
address collateralToken,
bytes32 parentCollectionId,
bytes32 conditionId,
uint256[] calldata partition,
uint256 amount
) external;

function redeemPositions(
address collateralToken,
bytes32 parentCollectionId,
bytes32 conditionId,
uint256[] calldata indexSets
) external;
}

interface INegRiskAdapter {
function redeemPositions(
bytes32 conditionId,
uint256[] calldata amounts
) external;
}
}

#[derive(Args)]
pub struct CtfArgs {
#[command(subcommand)]
Expand Down Expand Up @@ -171,7 +207,12 @@ fn binary_u256_vec() -> Vec<U256> {
DEFAULT_BINARY_SETS.iter().map(|&n| U256::from(n)).collect()
}

pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&str>) -> Result<()> {
pub async fn execute(
args: CtfArgs,
output: OutputFormat,
private_key: Option<&str>,
signature_type: Option<&str>,
) -> Result<()> {
match args.command {
CtfCommand::Split {
condition,
Expand All @@ -187,23 +228,24 @@ pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&s
None => binary_u256_vec(),
};

let provider = auth::create_provider(private_key).await?;
let client = ctf::Client::new(provider, POLYGON)?;

let req = SplitPositionRequest::builder()
.collateral_token(collateral)
.parent_collection_id(parent)
.condition_id(condition)
.partition(partition)
.amount(usdc_amount)
.build();

let resp = client
.split_position(&req)
.await
.context("Split position failed")?;

ctf_output::print_tx_result("split", resp.transaction_hash, resp.block_number, &output)
let proxy = proxy::is_proxy_mode(signature_type)?;
let config = polymarket_client_sdk::contract_config(POLYGON, false)
.context("CTF contract config not found")?;
let calldata = IConditionalTokens::splitPositionCall {
collateralToken: collateral,
parentCollectionId: parent,
conditionId: condition,
partition,
amount: usdc_amount,
}
.abi_encode();

let (tx_hash, block_number) =
proxy::send_call(private_key, proxy, config.conditional_tokens, calldata)
.await
.context("Split position failed")?;

ctf_output::print_tx_result("split", tx_hash, block_number, &output)
}
CtfCommand::Merge {
condition,
Expand All @@ -219,23 +261,24 @@ pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&s
None => binary_u256_vec(),
};

let provider = auth::create_provider(private_key).await?;
let client = ctf::Client::new(provider, POLYGON)?;

let req = MergePositionsRequest::builder()
.collateral_token(collateral)
.parent_collection_id(parent)
.condition_id(condition)
.partition(partition)
.amount(usdc_amount)
.build();

let resp = client
.merge_positions(&req)
.await
.context("Merge positions failed")?;

ctf_output::print_tx_result("merge", resp.transaction_hash, resp.block_number, &output)
let proxy = proxy::is_proxy_mode(signature_type)?;
let config = polymarket_client_sdk::contract_config(POLYGON, false)
.context("CTF contract config not found")?;
let calldata = IConditionalTokens::mergePositionsCall {
collateralToken: collateral,
parentCollectionId: parent,
conditionId: condition,
partition,
amount: usdc_amount,
}
.abi_encode();

let (tx_hash, block_number) =
proxy::send_call(private_key, proxy, config.conditional_tokens, calldata)
.await
.context("Merge positions failed")?;

ctf_output::print_tx_result("merge", tx_hash, block_number, &output)
}
CtfCommand::Redeem {
condition,
Expand All @@ -249,45 +292,44 @@ pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&s
None => binary_u256_vec(),
};

let provider = auth::create_provider(private_key).await?;
let client = ctf::Client::new(provider, POLYGON)?;

let req = RedeemPositionsRequest::builder()
.collateral_token(collateral)
.parent_collection_id(parent)
.condition_id(condition)
.index_sets(index_sets)
.build();

let resp = client
.redeem_positions(&req)
.await
.context("Redeem positions failed")?;

ctf_output::print_tx_result("redeem", resp.transaction_hash, resp.block_number, &output)
let proxy = proxy::is_proxy_mode(signature_type)?;
let config = polymarket_client_sdk::contract_config(POLYGON, false)
.context("CTF contract config not found")?;
let calldata = IConditionalTokens::redeemPositionsCall {
collateralToken: collateral,
parentCollectionId: parent,
conditionId: condition,
indexSets: index_sets,
}
.abi_encode();

let (tx_hash, block_number) =
proxy::send_call(private_key, proxy, config.conditional_tokens, calldata)
.await
.context("Redeem positions failed")?;

ctf_output::print_tx_result("redeem", tx_hash, block_number, &output)
}
CtfCommand::RedeemNegRisk { condition, amounts } => {
let amounts = parse_usdc_amounts(&amounts)?;

let provider = auth::create_provider(private_key).await?;
let client = ctf::Client::with_neg_risk(provider, POLYGON)?;

let req = RedeemNegRiskRequest::builder()
.condition_id(condition)
.amounts(amounts)
.build();

let resp = client
.redeem_neg_risk(&req)
let proxy = proxy::is_proxy_mode(signature_type)?;
let config = polymarket_client_sdk::contract_config(POLYGON, true)
.context("NegRisk contract config not found")?;
let target = config
.neg_risk_adapter
.context("NegRisk adapter not configured")?;
let calldata = INegRiskAdapter::redeemPositionsCall {
conditionId: condition,
amounts,
}
.abi_encode();

let (tx_hash, block_number) = proxy::send_call(private_key, proxy, target, calldata)
.await
.context("Redeem neg-risk positions failed")?;

ctf_output::print_tx_result(
"redeem-neg-risk",
resp.transaction_hash,
resp.block_number,
&output,
)
ctf_output::print_tx_result("redeem-neg-risk", tx_hash, block_number, &output)
}
CtfCommand::ConditionId {
oracle,
Expand Down
Loading
Loading