near_api/account/
create.rs1use std::convert::Infallible;
2
3use near_api_types::{
4 AccessKey, AccessKeyPermission, AccountId, Action, NearGas, NearToken, PublicKey,
5 transaction::{
6 PrepopulateTransaction,
7 actions::{AddKeyAction, CreateAccountAction, TransferAction},
8 },
9};
10use reqwest::Response;
11use serde_json::json;
12use url::Url;
13
14use crate::{
15 Contract, NetworkConfig,
16 common::send::Transactionable,
17 errors::{AccountCreationError, FaucetError, ValidationError},
18 transactions::{ConstructTransaction, TransactionWithSign},
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 ) -> PublicKeyProvider<TransactionWithSign<CreateAccountFundMyselfTx>, AccountCreationError>
35 {
36 PublicKeyProvider::new(Box::new(move |public_key| {
37 let (actions, receiver_id) = if self.account_id.is_sub_account_of(&signer_account_id) {
38 (
39 vec![
40 Action::CreateAccount(CreateAccountAction {}),
41 Action::Transfer(TransferAction {
42 deposit: initial_balance,
43 }),
44 Action::AddKey(Box::new(AddKeyAction {
45 public_key,
46 access_key: AccessKey {
47 nonce: 0.into(),
48 permission: AccessKeyPermission::FullAccess,
49 },
50 })),
51 ],
52 self.account_id.clone(),
53 )
54 } else if let Some(linkdrop_account_id) = self.account_id.get_parent_account_id() {
55 (
56 Contract(linkdrop_account_id.to_owned())
57 .call_function(
58 "create_account",
59 json!({
60 "new_account_id": self.account_id.to_string(),
61 "new_public_key": public_key,
62 }),
63 )?
64 .transaction()
65 .gas(NearGas::from_tgas(30))
66 .deposit(initial_balance)
67 .with_signer_account(signer_account_id.clone())
68 .prepopulated()
69 .actions,
70 linkdrop_account_id.to_owned(),
71 )
72 } else {
73 return Err(AccountCreationError::TopLevelAccountIsNotAllowed);
74 };
75
76 let prepopulated = ConstructTransaction::new(signer_account_id, receiver_id)
77 .add_actions(actions)
78 .prepopulated();
79
80 Ok(TransactionWithSign {
81 tx: CreateAccountFundMyselfTx { prepopulated },
82 })
83 }))
84 }
85
86 pub fn sponsor_by_faucet_service(self) -> PublicKeyProvider<CreateAccountByFaucet, Infallible> {
91 PublicKeyProvider::new(Box::new(move |public_key| {
92 Ok(CreateAccountByFaucet {
93 new_account_id: self.account_id,
94 public_key,
95 })
96 }))
97 }
98}
99
100#[derive(Clone, Debug)]
101pub struct CreateAccountByFaucet {
102 pub new_account_id: AccountId,
103 pub public_key: PublicKey,
104}
105
106impl CreateAccountByFaucet {
107 pub async fn send_to_testnet_faucet(self) -> Result<Response, FaucetError> {
111 let testnet = NetworkConfig::testnet();
112 self.send_to_config_faucet(&testnet).await
113 }
114
115 pub async fn send_to_config_faucet(
126 self,
127 config: &NetworkConfig,
128 ) -> Result<Response, FaucetError> {
129 let faucet_service_url = match &config.faucet_url {
130 Some(url) => url,
131 None => return Err(FaucetError::FaucetIsNotDefined(config.network_name.clone())),
132 };
133
134 self.send_to_faucet(faucet_service_url).await
135 }
136
137 pub async fn send_to_faucet(self, url: &Url) -> Result<Response, FaucetError> {
147 let mut data = std::collections::HashMap::new();
148 data.insert("newAccountId", self.new_account_id.to_string());
149 data.insert("newAccountPublicKey", self.public_key.to_string());
150
151 let client = reqwest::Client::new();
152
153 Ok(client.post(url.clone()).json(&data).send().await?)
154 }
155}
156
157#[derive(Clone, Debug)]
158pub struct CreateAccountFundMyselfTx {
159 prepopulated: PrepopulateTransaction,
160}
161
162#[async_trait::async_trait]
163impl Transactionable for CreateAccountFundMyselfTx {
164 fn prepopulated(&self) -> PrepopulateTransaction {
165 self.prepopulated.clone()
166 }
167
168 async fn validate_with_network(&self, network: &NetworkConfig) -> Result<(), ValidationError> {
169 if self
170 .prepopulated
171 .receiver_id
172 .is_sub_account_of(&self.prepopulated.signer_id)
173 {
174 return Ok(());
175 }
176
177 match &network.linkdrop_account_id {
178 Some(linkdrop) => {
179 if &self.prepopulated.receiver_id != linkdrop {
180 Err(AccountCreationError::AccountShouldBeSubAccountOfSignerOrLinkdrop)?;
181 }
182 }
183 None => Err(AccountCreationError::LinkdropIsNotDefined)?,
184 }
185
186 Ok(())
187 }
188}
189
190pub type PublicKeyCallback<T, E> = dyn FnOnce(PublicKey) -> Result<T, E>;
191
192pub struct PublicKeyProvider<T, E> {
193 next_step: Box<PublicKeyCallback<T, E>>,
194}
195
196impl<T, E> PublicKeyProvider<T, E> {
197 pub const fn new(next_step: Box<PublicKeyCallback<T, E>>) -> Self {
198 Self { next_step }
199 }
200
201 pub fn public_key(self, pk: impl Into<PublicKey>) -> Result<T, E> {
202 (self.next_step)(pk.into())
203 }
204}