Skip to content

Commit

Permalink
funding account support in job-account-tracker - user can hold multip…
Browse files Browse the repository at this point in the history
…le funding accounts - jobs can have a single funding account attached - recurring jobs ad hoc create funding accounts - user can optionally provide one
  • Loading branch information
simke9445 committed Nov 22, 2023
1 parent edbb294 commit f716cc8
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 38 deletions.
50 changes: 27 additions & 23 deletions contracts/warp-controller/src/execute/job.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::state::{JobQueue, STATE};
use crate::util::msg::build_account_execute_warp_msgs;
use crate::util::msg::{
build_account_execute_warp_msgs, build_free_funding_account_msg, build_take_funding_account_msg,
};
use crate::ContractError;
use controller::account::{AssetInfo, WarpMsgs};
use controller::job::{
Expand All @@ -24,7 +26,7 @@ use crate::{
};

use controller::{account::CwFund, Config};
use job_account_tracker::{Account, AccountResponse, AccountsResponse};
use job_account_tracker::{AccountResponse, FundingAccountResponse};
use resolver::QueryHydrateMsgsMsg;

use super::fee::{compute_burn_fee, compute_creation_fee, compute_maintenance_fee};
Expand Down Expand Up @@ -134,38 +136,40 @@ pub fn create_job(
},
)?;

let free_accounts: AccountsResponse = deps.querier.query_wasm_smart(
let job_account_resp: AccountResponse = deps.querier.query_wasm_smart(
job_account_tracker_address_ref,
&job_account_tracker::QueryMsg::QueryFreeAccounts(
job_account_tracker::QueryFreeAccountsMsg {
&job_account_tracker::QueryMsg::QueryFirstFreeAccount(
job_account_tracker::QueryFirstFreeAccountMsg {
account_owner_addr: job_owner.to_string(),
start_after: None,
limit: Some(2),
},
),
)?;

let job_account = free_accounts.accounts.get(0);

let funding_account: Option<Account>;
let funding_account_resp: FundingAccountResponse;

if let Some(funding_account_addr) = data.funding_account {
// fetch funding account and check if it exists, throw otherwise
let funding_account_response: AccountResponse = deps.querier.query_wasm_smart(
funding_account_resp = deps.querier.query_wasm_smart(
job_account_tracker_address_ref,
&job_account_tracker::QueryMsg::QueryFreeAccount(
job_account_tracker::QueryFreeAccountMsg {
&job_account_tracker::QueryMsg::QueryFundingAccount(
job_account_tracker::QueryFundingAccountMsg {
account_addr: funding_account_addr.to_string(),
account_owner_addr: info.sender.to_string(),
},
),
)?;

funding_account = Some(funding_account_response.account.unwrap());
} else {
funding_account = free_accounts.accounts.get(1).cloned();
funding_account_resp = deps.querier.query_wasm_smart(
job_account_tracker_address_ref,
&job_account_tracker::QueryMsg::QueryFirstFreeFundingAccount(
job_account_tracker::QueryFirstFreeFundingAccountMsg {
account_owner_addr: job_owner.to_string(),
},
),
)?;
}

match job_account {
match job_account_resp.account {
None => {
// Create account then create job in reply
submsgs.push(SubMsg {
Expand Down Expand Up @@ -266,7 +270,7 @@ pub fn create_job(
}

if data.recurring {
match funding_account {
match funding_account_resp.funding_account {
None => {
// Create funding account then create job in reply
submsgs.push(SubMsg {
Expand All @@ -290,8 +294,8 @@ pub fn create_job(
attrs.push(Attribute::new("action", "create_funding_account_and_job"));
}
Some(available_account) => {
let available_account_addr = &available_account.addr;
// Update job.account from placeholder value to job account
let available_account_addr = &available_account.account_addr;
// Update funding_account from placeholder value to funding account
job.funding_account = Some(available_account_addr.clone());
JobQueue::sync(&mut deps, env, job.clone())?;

Expand All @@ -305,7 +309,7 @@ pub fn create_job(
));

// Take account
msgs.push(build_taken_account_msg(
msgs.push(build_take_funding_account_msg(
config.job_account_tracker_address.to_string(),
job_owner.to_string(),
available_account_addr.to_string(),
Expand Down Expand Up @@ -401,7 +405,7 @@ pub fn delete_job(
));

if let Some(funding_account) = job.funding_account {
msgs.push(build_free_account_msg(
msgs.push(build_free_funding_account_msg(
config.job_account_tracker_address.to_string(),
job.owner.to_string(),
funding_account.to_string(),
Expand Down Expand Up @@ -555,7 +559,7 @@ pub fn execute_job(
));

if let Some(funding_account) = job.funding_account {
msgs.push(build_free_account_msg(
msgs.push(build_free_funding_account_msg(
config.job_account_tracker_address.to_string(),
job.owner.to_string(),
funding_account.to_string(),
Expand Down
6 changes: 3 additions & 3 deletions contracts/warp-controller/src/reply/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
state::JobQueue,
util::msg::{
build_account_execute_warp_msgs, build_taken_account_msg, build_transfer_cw20_msg,
build_transfer_cw721_msg, build_transfer_native_funds_msg,
build_transfer_cw721_msg, build_transfer_native_funds_msg, build_take_funding_account_msg,
},
ContractError,
};
Expand Down Expand Up @@ -226,8 +226,8 @@ pub fn create_funding_account_and_job(
))
}

// Take job account
msgs.push(build_taken_account_msg(
// Take funding account
msgs.push(build_take_funding_account_msg(
config.job_account_tracker_address.to_string(),
job.owner.to_string(),
funding_account_addr.to_string(),
Expand Down
4 changes: 2 additions & 2 deletions contracts/warp-controller/src/reply/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
legacy_account::is_legacy_account,
msg::{
build_account_execute_generic_msgs, build_account_withdraw_assets_msg,
build_taken_account_msg, build_transfer_native_funds_msg,
build_taken_account_msg, build_transfer_native_funds_msg, build_take_funding_account_msg,
},
},
ContractError,
Expand Down Expand Up @@ -233,7 +233,7 @@ pub fn execute_job(
));

// take funding account with new job
msgs.push(build_taken_account_msg(
msgs.push(build_take_funding_account_msg(
config.job_account_tracker_address.to_string(),
finished_job.owner.to_string(),
funding_account_addr.to_string(),
Expand Down
44 changes: 43 additions & 1 deletion contracts/warp-controller/src/util/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use controller::account::{
WithdrawAssetsMsg,
};
use job_account::GenericMsg;
use job_account_tracker::{FreeAccountMsg, TakeAccountMsg};
use job_account_tracker::{
FreeAccountMsg, FreeFundingAccountMsg, TakeAccountMsg, TakeFundingAccountMsg,
};

#[allow(clippy::too_many_arguments)]
pub fn build_instantiate_warp_account_msg(
Expand Down Expand Up @@ -73,6 +75,46 @@ pub fn build_taken_account_msg(
})
}

pub fn build_free_funding_account_msg(
job_account_tracker_addr: String,
account_owner_addr: String,
account_addr: String,
job_id: Uint64,
) -> CosmosMsg {
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: job_account_tracker_addr,
msg: to_binary(&job_account_tracker::ExecuteMsg::FreeFundingAccount(
FreeFundingAccountMsg {
account_owner_addr,
account_addr,
job_id,
},
))
.unwrap(),
funds: vec![],
})
}

pub fn build_take_funding_account_msg(
job_account_tracker_addr: String,
account_owner_addr: String,
account_addr: String,
job_id: Uint64,
) -> CosmosMsg {
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: job_account_tracker_addr,
msg: to_binary(&job_account_tracker::ExecuteMsg::TakeFundingAccount(
TakeFundingAccountMsg {
account_owner_addr,
account_addr,
job_id,
},
))
.unwrap(),
funds: vec![],
})
}

pub fn build_transfer_cw20_msg(
cw20_token_contract_addr: String,
owner_addr: String,
Expand Down
17 changes: 17 additions & 0 deletions contracts/warp-job-account-tracker/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ pub fn execute(
nonpayable(&info).unwrap();
execute::account::free_account(deps, data)
}
ExecuteMsg::TakeFundingAccount(data) => {
nonpayable(&info).unwrap();
execute::account::take_funding_account(deps, data)
}
ExecuteMsg::FreeFundingAccount(data) => {
nonpayable(&info).unwrap();
execute::account::free_funding_account(deps, data)
}
}
}

Expand All @@ -71,6 +79,15 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
QueryMsg::QueryFreeAccount(data) => {
to_binary(&query::account::query_free_account(deps, data)?)
}
QueryMsg::QueryFundingAccounts(data) => {
to_binary(&query::account::query_funding_accounts(deps, data)?)
}
QueryMsg::QueryFundingAccount(data) => {
to_binary(&query::account::query_funding_account(deps, data)?)
}
QueryMsg::QueryFirstFreeFundingAccount(data) => {
to_binary(&query::account::query_first_free_funding_account(deps, data)?)
}
}
}

Expand Down
9 changes: 6 additions & 3 deletions contracts/warp-job-account-tracker/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,17 @@ pub enum ContractError {
#[error("Error resolving JSON path")]
ResolveError {},

#[error("Sub account already taken")]
#[error("Account already taken")]
AccountAlreadyTakenError {},

#[error("Sub account already free")]
#[error("Account already free")]
AccountAlreadyFreeError {},

#[error("Sub account should be taken but it is free")]
#[error("Account should be taken but it is free")]
AccountNotTakenError {},

#[error("Account not found")]
AccountNotFound {},
}

impl From<serde_json_wasm::de::Error> for ContractError {
Expand Down
99 changes: 97 additions & 2 deletions contracts/warp-job-account-tracker/src/execute/account.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::state::{FREE_ACCOUNTS, TAKEN_ACCOUNTS};
use crate::state::{FREE_ACCOUNTS, FUNDING_ACCOUNTS, TAKEN_ACCOUNTS, TAKEN_FUNDING_ACCOUNT_BY_JOB};
use crate::ContractError;
use cosmwasm_std::{DepsMut, Response};
use job_account_tracker::{FreeAccountMsg, TakeAccountMsg};
use job_account_tracker::{
FreeAccountMsg, FreeFundingAccountMsg, FundingAccount, TakeAccountMsg, TakeFundingAccountMsg,
};

pub fn taken_account(deps: DepsMut, data: TakeAccountMsg) -> Result<Response, ContractError> {
let account_owner_ref = &deps.api.addr_validate(data.account_owner_addr.as_str())?;
Expand Down Expand Up @@ -38,3 +40,96 @@ pub fn free_account(deps: DepsMut, data: FreeAccountMsg) -> Result<Response, Con
.add_attribute("action", "free_account")
.add_attribute("account_addr", data.account_addr))
}

pub fn take_funding_account(
deps: DepsMut,
data: TakeFundingAccountMsg,
) -> Result<Response, ContractError> {
let account_owner_addr_ref = deps.api.addr_validate(&data.account_owner_addr)?;
let account_addr_ref = &deps.api.addr_validate(data.account_addr.as_str())?;

TAKEN_FUNDING_ACCOUNT_BY_JOB.update(deps.storage, data.job_id.u64(), |s| match s {
// value is a dummy data because there is no built in support for set in cosmwasm
None => Ok(account_addr_ref.clone()),
Some(_) => Err(ContractError::AccountAlreadyTakenError {}),
})?;

FUNDING_ACCOUNTS.update(
deps.storage,
&account_owner_addr_ref,
|accounts_opt| -> Result<Vec<FundingAccount>, ContractError> {
match accounts_opt {
None => {
// No funding accounts exist for this user, create a new vec
Ok(vec![FundingAccount {
account_addr: account_addr_ref.clone(),
taken_by_job_ids: vec![data.job_id],
}])
}
Some(mut accounts) => {
// Check if a funding account with the specified address already exists
if let Some(funding_account) = accounts
.iter_mut()
.find(|acc| acc.account_addr == account_addr_ref.clone())
{
// Funding account exists, update its job_ids
funding_account.taken_by_job_ids.push(data.job_id);
} else {
// Funding account does not exist, add a new one
accounts.push(FundingAccount {
account_addr: account_addr_ref.clone(),
taken_by_job_ids: vec![data.job_id],
});
}
Ok(accounts)
}
}
},
)?;

Ok(Response::new()
.add_attribute("action", "take_funding_account")
.add_attribute("account_addr", data.account_addr)
.add_attribute("job_id", data.job_id.to_string()))
}

pub fn free_funding_account(
deps: DepsMut,
data: FreeFundingAccountMsg,
) -> Result<Response, ContractError> {
let account_owner_addr_ref = deps.api.addr_validate(&data.account_owner_addr)?;
let account_addr_ref = deps.api.addr_validate(&data.account_addr)?;

TAKEN_FUNDING_ACCOUNT_BY_JOB.remove(deps.storage, data.job_id.u64());

FUNDING_ACCOUNTS.update(
deps.storage,
&account_owner_addr_ref,
|accounts_opt| -> Result<Vec<FundingAccount>, ContractError> {
match accounts_opt {
Some(mut accounts) => {
// Find the funding account with the specified address
if let Some(funding_account) = accounts
.iter_mut()
.find(|acc| acc.account_addr == account_addr_ref)
{
// Remove the specified job ID
funding_account
.taken_by_job_ids
.retain(|&id| id != data.job_id);

Ok(accounts)
} else {
Err(ContractError::AccountNotFound {})
}
}
None => Err(ContractError::AccountNotFound {}),
}
},
)?;

Ok(Response::new()
.add_attribute("action", "free_funding_account")
.add_attribute("account_addr", data.account_addr)
.add_attribute("job_id", data.job_id.to_string()))
}
Loading

0 comments on commit f716cc8

Please sign in to comment.