near_api/account/
create.rs1use std::convert::Infallible;
2
3use near_api_types::{
4 transaction::{
5 actions::{AddKeyAction, CreateAccountAction, TransferAction},
6 PrepopulateTransaction,
7 },
8 AccessKey, AccessKeyPermission, AccountId, Action, NearGas, NearToken, PublicKey,
9};
10use reqwest::Response;
11use serde_json::json;
12use url::Url;
13
14use crate::{
15 common::send::Transactionable,
16 errors::{AccountCreationError, ArgumentValidationError, FaucetError, ValidationError},
17 transactions::{ConstructTransaction, TransactionWithSign},
18 Contract, NetworkConfig,
19};
20
21#[derive(Clone, Debug)]
22pub struct CreateAccountBuilder {
23 pub account_id: AccountId,
24}
25
26impl CreateAccountBuilder {
27 pub fn fund_myself(
31 self,
32 signer_account_id: AccountId,
33 initial_balance: NearToken,
34 ) -> FundMyselfBuilder {
35 FundMyselfBuilder {
36 new_account_id: self.account_id,
37 signer_account_id,
38 initial_balance,
39 }
40 }
41
42 pub fn sponsor_by_faucet_service(self) -> SponsorByFaucetServiceBuilder {
47 SponsorByFaucetServiceBuilder {
48 new_account_id: self.account_id,
49 }
50 }
51}
52
53#[derive(Clone, Debug)]
54pub struct FundMyselfBuilder {
55 new_account_id: AccountId,
56 signer_account_id: AccountId,
57 initial_balance: NearToken,
58}
59
60impl FundMyselfBuilder {
61 pub fn with_public_key(
65 self,
66 pk: impl Into<PublicKey>,
67 ) -> TransactionWithSign<CreateAccountFundMyselfTx> {
68 let public_key = pk.into();
69 let transaction = if self
70 .new_account_id
71 .is_sub_account_of(&self.signer_account_id)
72 {
73 ConstructTransaction::new(self.signer_account_id.clone(), self.new_account_id.clone())
74 .add_actions(vec![
75 Action::CreateAccount(CreateAccountAction {}),
76 Action::Transfer(TransferAction {
77 deposit: self.initial_balance,
78 }),
79 Action::AddKey(Box::new(AddKeyAction {
80 public_key,
81 access_key: AccessKey {
82 nonce: 0.into(),
83 permission: AccessKeyPermission::FullAccess,
84 },
85 })),
86 ])
87 .transaction
88 } else if let Some(linkdrop_account_id) = self.new_account_id.get_parent_account_id() {
89 Contract(linkdrop_account_id.to_owned())
90 .call_function(
91 "create_account",
92 json!({
93 "new_account_id": self.new_account_id.to_string(),
94 "new_public_key": public_key,
95 }),
96 )
97 .transaction()
98 .gas(NearGas::from_tgas(30))
99 .deposit(self.initial_balance)
100 .with_signer_account(self.signer_account_id.clone())
101 .transaction
102 } else {
103 Err(AccountCreationError::TopLevelAccountIsNotAllowed.into())
104 };
105
106 TransactionWithSign {
107 tx: CreateAccountFundMyselfTx {
108 prepopulated: transaction,
109 },
110 }
111 }
112}
113
114#[derive(Clone, Debug)]
115pub struct SponsorByFaucetServiceBuilder {
116 new_account_id: AccountId,
117}
118
119impl SponsorByFaucetServiceBuilder {
120 pub fn with_public_key(
124 self,
125 pk: impl Into<PublicKey>,
126 ) -> Result<CreateAccountByFaucet, Infallible> {
127 Ok(CreateAccountByFaucet {
128 new_account_id: self.new_account_id,
129 public_key: pk.into(),
130 })
131 }
132}
133
134#[derive(Clone, Debug)]
135pub struct CreateAccountByFaucet {
136 pub new_account_id: AccountId,
137 pub public_key: PublicKey,
138}
139
140impl CreateAccountByFaucet {
141 pub async fn send_to_testnet_faucet(self) -> Result<Response, FaucetError> {
145 let testnet = NetworkConfig::testnet();
146 self.send_to_config_faucet(&testnet).await
147 }
148
149 pub async fn send_to_config_faucet(
160 self,
161 config: &NetworkConfig,
162 ) -> Result<Response, FaucetError> {
163 let faucet_service_url = match &config.faucet_url {
164 Some(url) => url,
165 None => return Err(FaucetError::FaucetIsNotDefined(config.network_name.clone())),
166 };
167
168 self.send_to_faucet(faucet_service_url).await
169 }
170
171 pub async fn send_to_faucet(self, url: &Url) -> Result<Response, FaucetError> {
181 let mut data = std::collections::HashMap::new();
182 data.insert("newAccountId", self.new_account_id.to_string());
183 data.insert("newAccountPublicKey", self.public_key.to_string());
184
185 let client = reqwest::Client::new();
186
187 Ok(client.post(url.clone()).json(&data).send().await?)
188 }
189}
190
191#[derive(Clone, Debug)]
197pub struct CreateAccountFundMyselfTx {
198 prepopulated: Result<PrepopulateTransaction, ArgumentValidationError>,
199}
200
201#[async_trait::async_trait]
202impl Transactionable for CreateAccountFundMyselfTx {
203 fn prepopulated(&self) -> Result<PrepopulateTransaction, ArgumentValidationError> {
204 self.prepopulated.clone()
205 }
206
207 async fn validate_with_network(&self, network: &NetworkConfig) -> Result<(), ValidationError> {
208 let prepopulated = self.prepopulated()?;
209
210 if prepopulated
211 .receiver_id
212 .is_sub_account_of(&prepopulated.signer_id)
213 {
214 return Ok(());
215 }
216
217 match &network.linkdrop_account_id {
218 Some(linkdrop) => {
219 if &prepopulated.receiver_id != linkdrop {
220 Err(AccountCreationError::AccountShouldBeSubAccountOfSignerOrLinkdrop)?;
221 }
222 }
223 None => Err(AccountCreationError::LinkdropIsNotDefined)?,
224 }
225
226 Ok(())
227 }
228}