create_contract.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. // SPDX-License-Identifier: Apache-2.0
  2. use crate::{
  3. account_new, build_solidity, create_program_address, Account, AccountMeta, AccountState,
  4. BorshToken, Pubkey,
  5. };
  6. use base58::{FromBase58, ToBase58};
  7. #[test]
  8. fn simple_create_contract_no_seed() {
  9. let mut vm = build_solidity(
  10. r#"
  11. contract bar0 {
  12. function test_other() external returns (bar1) {
  13. bar1 x = new bar1("yo from bar0");
  14. return x;
  15. }
  16. function call_bar1_at_address(bar1 a, string x) public {
  17. a.say_hello(x);
  18. }
  19. }
  20. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  21. contract bar1 {
  22. @payer(payer)
  23. constructor(string v) {
  24. print("bar1 says: " + v);
  25. }
  26. function say_hello(string v) public {
  27. print("Hello {}".format(v));
  28. }
  29. }"#,
  30. );
  31. vm.set_program(0);
  32. let data_account = vm.initialize_data_account();
  33. vm.function("new")
  34. .accounts(vec![("dataAccount", data_account)])
  35. .call();
  36. let program_id: Account = "CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT"
  37. .from_base58()
  38. .unwrap()
  39. .try_into()
  40. .unwrap();
  41. let acc = account_new();
  42. let payer = account_new();
  43. println!("new account: {}", acc.to_base58());
  44. vm.account_data.insert(payer, AccountState::default());
  45. vm.account_data.insert(
  46. acc,
  47. AccountState {
  48. data: Vec::new(),
  49. owner: Some(program_id),
  50. lamports: 0,
  51. },
  52. );
  53. let bar1 = vm
  54. .function("test_other")
  55. .accounts(vec![
  56. ("bar1_dataAccount", acc),
  57. ("bar1_programId", program_id),
  58. ("payer", payer),
  59. ("systemProgram", [0; 32]),
  60. ])
  61. .remaining_accounts(&[AccountMeta {
  62. pubkey: Pubkey(acc),
  63. is_writable: true,
  64. is_signer: true,
  65. }])
  66. .call()
  67. .unwrap();
  68. assert_eq!(vm.logs, "bar1 says: yo from bar0");
  69. assert_eq!(vm.account_data[&acc].data.len(), 16);
  70. vm.logs.truncate(0);
  71. vm.function("call_bar1_at_address")
  72. .arguments(&[bar1, BorshToken::String(String::from("xywoleh"))])
  73. .accounts(vec![
  74. ("bar1_programId", program_id),
  75. ("systemProgram", [0; 32]),
  76. ])
  77. .call();
  78. assert_eq!(vm.logs, "Hello xywoleh");
  79. }
  80. #[test]
  81. fn simple_create_contract() {
  82. let mut vm = build_solidity(
  83. r#"
  84. contract bar0 {
  85. function test_other() external returns (bar1) {
  86. bar1 x = new bar1("yo from bar0");
  87. return x;
  88. }
  89. function call_bar1_at_address(bar1 a, string x) public {
  90. a.say_hello(x);
  91. }
  92. }
  93. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  94. contract bar1 {
  95. @payer(pay)
  96. constructor(string v) {
  97. print("bar1 says: " + v);
  98. }
  99. function say_hello(string v) public {
  100. print("Hello {}".format(v));
  101. }
  102. }"#,
  103. );
  104. vm.set_program(0);
  105. let data_account = vm.initialize_data_account();
  106. vm.function("new")
  107. .accounts(vec![("dataAccount", data_account)])
  108. .call();
  109. let program_id: Account = "CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT"
  110. .from_base58()
  111. .unwrap()
  112. .try_into()
  113. .unwrap();
  114. let seed = vm.create_pda(&program_id, 7);
  115. let payer = account_new();
  116. vm.account_data.insert(payer, AccountState::default());
  117. let bar1 = vm
  118. .function("test_other")
  119. .accounts(vec![
  120. ("bar1_dataAccount", seed.0),
  121. ("bar1_programId", program_id),
  122. ("pay", payer),
  123. ("systemProgram", [0; 32]),
  124. ])
  125. .call()
  126. .unwrap();
  127. assert_eq!(vm.logs, "bar1 says: yo from bar0");
  128. vm.logs.truncate(0);
  129. println!("next test, {bar1:?}");
  130. vm.function("call_bar1_at_address")
  131. .arguments(&[bar1, BorshToken::String(String::from("xywoleh"))])
  132. .accounts(vec![
  133. ("bar1_programId", program_id),
  134. ("systemProgram", [0; 32]),
  135. ])
  136. .call();
  137. assert_eq!(vm.logs, "Hello xywoleh");
  138. }
  139. #[test]
  140. fn create_contract_wrong_program_id() {
  141. let mut vm = build_solidity(
  142. r#"
  143. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  144. contract bar0 {}
  145. "#,
  146. );
  147. let data_account = vm.initialize_data_account();
  148. vm.function("new")
  149. .accounts(vec![("dataAccount", data_account)])
  150. .call();
  151. let program = &vm.programs[0].id;
  152. let code = vm.account_data[program].data.clone();
  153. let mut vm = build_solidity(
  154. r#"
  155. @program_id("25UGQeMKp1YH8dR1WBtaj26iqfc49xjwfvLnUKavcz8E")
  156. contract bar0 {}
  157. "#,
  158. );
  159. let program = &vm.programs[0].id;
  160. vm.account_data.get_mut(program).unwrap().data = code;
  161. let data_account = vm.initialize_data_account();
  162. vm.function("new")
  163. .accounts(vec![("dataAccount", data_account)])
  164. .expected(7 << 32)
  165. .call();
  166. assert_eq!(
  167. vm.logs,
  168. "program_id should be CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT"
  169. );
  170. }
  171. #[test]
  172. fn call_constructor_twice() {
  173. let mut vm = build_solidity(
  174. r#"
  175. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  176. contract bar0 {}
  177. "#,
  178. );
  179. let data_account = vm.initialize_data_account();
  180. vm.function("new")
  181. .accounts(vec![("dataAccount", data_account)])
  182. .call();
  183. vm.function("new")
  184. .accounts(vec![("dataAccount", data_account)])
  185. .expected(2)
  186. .call();
  187. }
  188. #[test]
  189. fn create_contract_with_payer() {
  190. let mut vm = build_solidity(
  191. r#"
  192. contract x {
  193. uint64 v;
  194. @payer(p)
  195. constructor() {
  196. v = 102;
  197. }
  198. function f() public returns (uint64) {
  199. return v;
  200. }
  201. }"#,
  202. );
  203. let payer = account_new();
  204. vm.account_data.insert(payer, AccountState::default());
  205. let data_account = vm.initialize_data_account();
  206. vm.function("new")
  207. .accounts(vec![
  208. ("dataAccount", data_account),
  209. ("p", payer),
  210. ("systemProgram", [0; 32]),
  211. ])
  212. .call();
  213. let ret = vm
  214. .function("f")
  215. .accounts(vec![("dataAccount", data_account)])
  216. .call()
  217. .unwrap();
  218. assert_eq!(
  219. ret,
  220. BorshToken::Uint {
  221. width: 64,
  222. value: 102.into()
  223. }
  224. );
  225. }
  226. #[test]
  227. #[should_panic(expected = "external call failed")]
  228. // 64424509440 = 15 << 32 (ERROR_NEW_ACCOUNT_NEEDED)
  229. fn missing_contract() {
  230. let mut vm = build_solidity(
  231. r#"
  232. contract bar0 {
  233. function test_other() external returns (bar1) {
  234. bar1 x = new bar1("yo from bar0");
  235. return x;
  236. }
  237. function call_bar1_at_address(bar1 a, string x) public {
  238. a.say_hello(x);
  239. }
  240. }
  241. @program_id("7vJKRaKLGCNUPuHWdeHCTknkYf3dHXXEZ6ri7dc6ngeV")
  242. contract bar1 {
  243. constructor(string v) {
  244. print("bar1 says: " + v);
  245. }
  246. function say_hello(string v) public {
  247. print("Hello {}".format(v));
  248. }
  249. }"#,
  250. );
  251. vm.set_program(0);
  252. let data_account = vm.initialize_data_account();
  253. vm.function("new")
  254. .accounts(vec![("dataAccount", data_account)])
  255. .call();
  256. let missing = account_new();
  257. vm.logs.clear();
  258. vm.account_data.insert(missing, AccountState::default());
  259. let program_id: Account = "7vJKRaKLGCNUPuHWdeHCTknkYf3dHXXEZ6ri7dc6ngeV"
  260. .from_base58()
  261. .unwrap()
  262. .try_into()
  263. .unwrap();
  264. // There is no payer account, so the external call fails.
  265. let _ = vm
  266. .function("test_other")
  267. .accounts(vec![
  268. ("bar1_programId", program_id),
  269. ("bar1_dataAccount", missing),
  270. ("systemProgram", [0; 32]),
  271. ])
  272. .must_fail();
  273. }
  274. #[test]
  275. fn two_contracts() {
  276. let mut vm = build_solidity(
  277. r#"
  278. import 'solana';
  279. contract bar0 {
  280. function test_other(address a, address b, address payer) external returns (bar1) {
  281. AccountMeta[2] bar1_metas = [
  282. AccountMeta({pubkey: a, is_writable: true, is_signer: true}),
  283. AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
  284. ];
  285. AccountMeta[2] bar2_metas = [
  286. AccountMeta({pubkey: b, is_writable: true, is_signer: true}),
  287. AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
  288. ];
  289. bar1 x = new bar1{accounts: bar1_metas}("yo from bar0");
  290. bar1 y = new bar1{accounts: bar2_metas}("hi from bar0");
  291. return x;
  292. }
  293. }
  294. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  295. contract bar1 {
  296. @payer(payer_account)
  297. constructor(string v) {
  298. print("bar1 says: " + v);
  299. }
  300. }"#,
  301. );
  302. vm.set_program(0);
  303. let data_account = vm.initialize_data_account();
  304. vm.function("new")
  305. .accounts(vec![("dataAccount", data_account)])
  306. .call();
  307. let program_id: Account = "CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT"
  308. .from_base58()
  309. .unwrap()
  310. .try_into()
  311. .unwrap();
  312. let seed1 = vm.create_pda(&program_id, 5);
  313. let seed2 = vm.create_pda(&program_id, 5);
  314. let payer = account_new();
  315. vm.account_data.insert(seed1.0, AccountState::default());
  316. vm.account_data.insert(seed2.0, AccountState::default());
  317. vm.account_data.insert(payer, AccountState::default());
  318. let _bar1 = vm
  319. .function("test_other")
  320. .arguments(&[
  321. BorshToken::Address(seed1.0),
  322. BorshToken::Address(seed2.0),
  323. BorshToken::Address(payer),
  324. ])
  325. .accounts(vec![("systemProgram", [0; 32])])
  326. .remaining_accounts(&[
  327. AccountMeta {
  328. pubkey: Pubkey(seed1.0),
  329. is_signer: true,
  330. is_writable: true,
  331. },
  332. AccountMeta {
  333. pubkey: Pubkey(seed2.0),
  334. is_signer: true,
  335. is_writable: true,
  336. },
  337. AccountMeta {
  338. pubkey: Pubkey(program_id),
  339. is_writable: false,
  340. is_signer: false,
  341. },
  342. AccountMeta {
  343. pubkey: Pubkey(payer),
  344. is_signer: true,
  345. is_writable: true,
  346. },
  347. ])
  348. .call();
  349. assert_eq!(vm.logs, "bar1 says: yo from bar0bar1 says: hi from bar0");
  350. vm.logs.truncate(0);
  351. }
  352. #[test]
  353. fn account_too_small() {
  354. let mut vm = build_solidity(
  355. r#"
  356. contract bar {
  357. int[200] foo1;
  358. }"#,
  359. );
  360. let data_account = vm.initialize_data_account();
  361. vm.account_data
  362. .get_mut(&data_account)
  363. .unwrap()
  364. .data
  365. .truncate(100);
  366. vm.function("new")
  367. .accounts(vec![("dataAccount", data_account)])
  368. .expected(5 << 32)
  369. .call();
  370. }
  371. #[test]
  372. fn account_with_space() {
  373. let mut vm = build_solidity(
  374. r#"
  375. contract bar {
  376. @payer(payer)
  377. constructor(@space uint64 x) {}
  378. function hello() public returns (bool) {
  379. return true;
  380. }
  381. }
  382. "#,
  383. );
  384. let data_account = vm.initialize_data_account();
  385. vm.account_data
  386. .get_mut(&data_account)
  387. .unwrap()
  388. .data
  389. .truncate(0);
  390. let payer = account_new();
  391. vm.account_data.insert(payer, AccountState::default());
  392. vm.function("new")
  393. .accounts(vec![
  394. ("dataAccount", data_account),
  395. ("payer", payer),
  396. ("systemProgram", [0; 32]),
  397. ])
  398. .arguments(&[BorshToken::Uint {
  399. width: 64,
  400. value: 306.into(),
  401. }])
  402. .call();
  403. assert_eq!(
  404. vm.account_data.get_mut(&data_account).unwrap().data.len(),
  405. 306
  406. );
  407. let ret = vm.function("hello").call().unwrap();
  408. assert_eq!(ret, BorshToken::Bool(true));
  409. }
  410. #[test]
  411. fn account_with_seed() {
  412. let mut vm = build_solidity(
  413. r#"
  414. contract bar {
  415. @space(511 + 102)
  416. @payer(payer)
  417. constructor(@seed bytes seed) {}
  418. function hello() public returns (bool) {
  419. return true;
  420. }
  421. }
  422. "#,
  423. );
  424. let program_id = vm.stack[0].id;
  425. let seed = vm.create_pda(&program_id, 7);
  426. let payer = account_new();
  427. vm.account_data.insert(payer, AccountState::default());
  428. vm.function("new")
  429. .accounts(vec![
  430. ("dataAccount", seed.0),
  431. ("payer", payer),
  432. ("systemProgram", [0; 32]),
  433. ])
  434. .arguments(&[BorshToken::Bytes(seed.1)])
  435. .call();
  436. assert_eq!(
  437. vm.account_data.get_mut(&seed.0).unwrap().data.len(),
  438. 511 + 102
  439. );
  440. let ret = vm.function("hello").call().unwrap();
  441. assert_eq!(ret, BorshToken::Bool(true));
  442. }
  443. #[test]
  444. fn account_with_seed_bump() {
  445. let mut vm = build_solidity(
  446. r#"
  447. contract bar {
  448. @space(511 + 102)
  449. @payer(payer)
  450. constructor(@seed address seed, @seed bytes2 seed2, @bump byte b) {}
  451. function hello() public returns (bool) {
  452. return true;
  453. }
  454. }
  455. "#,
  456. );
  457. let program_id = vm.stack[0].id;
  458. let (address, full_seed) = vm.create_pda(&program_id, 35);
  459. let bump = full_seed[34];
  460. let seed_addr = &full_seed[0..32];
  461. let seed2 = &full_seed[32..34];
  462. let payer = account_new();
  463. vm.account_data.insert(payer, AccountState::default());
  464. vm.function("new")
  465. .arguments(&[
  466. BorshToken::Address(seed_addr.try_into().unwrap()),
  467. BorshToken::FixedBytes(seed2.to_vec()),
  468. BorshToken::Uint {
  469. width: 8,
  470. value: bump.into(),
  471. },
  472. ])
  473. .accounts(vec![
  474. ("dataAccount", address),
  475. ("payer", payer),
  476. ("systemProgram", [0; 32]),
  477. ])
  478. .call();
  479. assert_eq!(
  480. vm.account_data.get_mut(&address).unwrap().data.len(),
  481. 511 + 102
  482. );
  483. let ret = vm.function("hello").call().unwrap();
  484. assert_eq!(ret, BorshToken::Bool(true));
  485. }
  486. #[test]
  487. fn account_with_seed_bump_literals() {
  488. let mut vm = build_solidity(
  489. r#"
  490. @program_id("vS5Tf8mnHGbUCMLQWrnvsFvwHLfA5p3yQM3ozxPckn8")
  491. contract bar {
  492. @space(2 << 8 + 4)
  493. @seed("meh")
  494. @bump(33) // 33 = ascii !
  495. @payer(my_account)
  496. constructor() {}
  497. function hello() public returns (bool) {
  498. return true;
  499. }
  500. }
  501. "#,
  502. );
  503. let program_id = vm.stack[0].id;
  504. let account = create_program_address(&program_id, &[b"meh!"]);
  505. let payer = account_new();
  506. vm.create_empty_account(&account.0, &program_id);
  507. vm.account_data.insert(payer, AccountState::default());
  508. vm.function("new")
  509. .accounts(vec![
  510. ("dataAccount", account.0),
  511. ("my_account", payer),
  512. ("systemProgram", [0; 32]),
  513. ])
  514. .call();
  515. assert_eq!(
  516. vm.account_data.get_mut(&account.0).unwrap().data.len(),
  517. 8192
  518. );
  519. let ret = vm.function("hello").call().unwrap();
  520. assert_eq!(ret, BorshToken::Bool(true));
  521. }
  522. #[test]
  523. fn create_child() {
  524. let mut vm = build_solidity(
  525. r#"
  526. contract creator {
  527. Child public c;
  528. function create_child() external {
  529. print("Going to create child");
  530. c = new Child();
  531. c.say_hello();
  532. }
  533. }
  534. @program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
  535. contract Child {
  536. @payer(payer)
  537. @space(511 + 7)
  538. constructor() {
  539. print("In child constructor");
  540. }
  541. function say_hello() pure public {
  542. print("Hello there");
  543. }
  544. }"#,
  545. );
  546. vm.set_program(0);
  547. let data_account = vm.initialize_data_account();
  548. vm.function("new")
  549. .accounts(vec![("dataAccount", data_account)])
  550. .call();
  551. let child_program_id: Account = "Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT"
  552. .from_base58()
  553. .unwrap()
  554. .try_into()
  555. .unwrap();
  556. let payer = account_new();
  557. let program_id = vm.stack[0].id;
  558. let seed = vm.create_pda(&program_id, 7);
  559. vm.account_data.insert(payer, AccountState::default());
  560. vm.account_data.insert(seed.0, AccountState::default());
  561. vm.function("create_child")
  562. .accounts(vec![
  563. ("Child_dataAccount", seed.0),
  564. ("Child_programId", child_program_id),
  565. ("dataAccount", data_account),
  566. ("payer", payer),
  567. ("systemProgram", [0; 32]),
  568. ])
  569. .remaining_accounts(&[AccountMeta {
  570. pubkey: Pubkey(seed.0),
  571. is_signer: true,
  572. is_writable: true,
  573. }])
  574. .call();
  575. assert_eq!(
  576. vm.logs,
  577. "Going to create childIn child constructorHello there"
  578. );
  579. }
  580. #[test]
  581. fn create_child_with_meta() {
  582. let mut vm = build_solidity(
  583. r#"
  584. import 'solana';
  585. contract creator {
  586. Child public c;
  587. function create_child_with_meta(address child, address payer) public {
  588. print("Going to create child");
  589. AccountMeta[2] metas = [
  590. AccountMeta({pubkey: child, is_signer: true, is_writable: true}),
  591. AccountMeta({pubkey: payer, is_signer: true, is_writable: true})
  592. // Passing the system account here crashes the VM, even if I add it to vm.account_data
  593. // AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false})
  594. ];
  595. c = new Child{accounts: metas}();
  596. c.say_hello();
  597. }
  598. }
  599. @program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
  600. contract Child {
  601. @payer(payer)
  602. @space(511 + 7)
  603. constructor() {
  604. print("In child constructor");
  605. }
  606. function say_hello() pure public {
  607. print("Hello there");
  608. }
  609. }
  610. "#,
  611. );
  612. vm.set_program(0);
  613. let data_account = vm.initialize_data_account();
  614. vm.function("new")
  615. .accounts(vec![("dataAccount", data_account)])
  616. .call();
  617. let payer = account_new();
  618. let program_id = vm.stack[0].id;
  619. let seed = vm.create_pda(&program_id, 7);
  620. vm.account_data.insert(seed.0, AccountState::default());
  621. vm.account_data.insert(payer, AccountState::default());
  622. let child_program_id: Account = "Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT"
  623. .from_base58()
  624. .unwrap()
  625. .try_into()
  626. .unwrap();
  627. vm.function("create_child_with_meta")
  628. .arguments(&[BorshToken::Address(seed.0), BorshToken::Address(payer)])
  629. .accounts(vec![
  630. ("Child_programId", child_program_id),
  631. ("dataAccount", data_account),
  632. ("systemProgram", [0; 32]),
  633. ])
  634. .remaining_accounts(&[
  635. AccountMeta {
  636. pubkey: Pubkey(seed.0),
  637. is_signer: false,
  638. is_writable: false,
  639. },
  640. AccountMeta {
  641. pubkey: Pubkey(payer),
  642. is_signer: true,
  643. is_writable: false,
  644. },
  645. ])
  646. .call();
  647. assert_eq!(
  648. vm.logs,
  649. "Going to create childIn child constructorHello there"
  650. );
  651. }