create-near-app 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/contracts/js/package.json +1 -1
- package/templates/contracts/js/src/contract.ts +3 -3
- package/templates/frontend/react/App.js +5 -5
- package/templates/frontend/react/index.js +19 -15
- package/templates/frontend/react/package.json +0 -1
- package/templates/frontend/react/ui-components.js +1 -1
- package/templates/frontend/shared/near-interface.js +10 -9
- package/templates/frontend/shared/near-wallet.js +20 -17
- package/templates/frontend/vanilla/index.html +7 -5
- package/templates/frontend/vanilla/index.js +12 -9
- package/templates/frontend/vanilla/package.json +5 -4
package/package.json
CHANGED
|
@@ -4,15 +4,15 @@ import { NearBindgen, near, call, view } from 'near-sdk-js';
|
|
|
4
4
|
class HelloNear {
|
|
5
5
|
greeting: string = "Hello";
|
|
6
6
|
|
|
7
|
-
@view // This method is read-only and can be called for free
|
|
7
|
+
@view({}) // This method is read-only and can be called for free
|
|
8
8
|
get_greeting(): string {
|
|
9
9
|
return this.greeting;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
@call // This method changes the state, for which it cost gas
|
|
12
|
+
@call({}) // This method changes the state, for which it cost gas
|
|
13
13
|
set_greeting({ message }: { message: string }): void {
|
|
14
14
|
// Record a log permanently to the blockchain!
|
|
15
15
|
near.log(`Saving greeting ${message}`);
|
|
16
16
|
this.greeting = message;
|
|
17
17
|
}
|
|
18
|
-
}
|
|
18
|
+
}
|
|
@@ -6,14 +6,14 @@ import './assets/global.css';
|
|
|
6
6
|
import { EducationalText, SignInPrompt, SignOutButton } from './ui-components';
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
export default function App({ isSignedIn,
|
|
9
|
+
export default function App({ isSignedIn, helloNEAR, wallet }) {
|
|
10
10
|
const [valueFromBlockchain, setValueFromBlockchain] = React.useState();
|
|
11
11
|
|
|
12
12
|
const [uiPleaseWait, setUiPleaseWait] = React.useState(true);
|
|
13
13
|
|
|
14
14
|
// Get blockchian state once on component load
|
|
15
15
|
React.useEffect(() => {
|
|
16
|
-
|
|
16
|
+
helloNEAR.getGreeting()
|
|
17
17
|
.then(setValueFromBlockchain)
|
|
18
18
|
.catch(alert)
|
|
19
19
|
.finally(() => {
|
|
@@ -31,8 +31,8 @@ export default function App({ isSignedIn, contract, wallet }) {
|
|
|
31
31
|
e.preventDefault();
|
|
32
32
|
setUiPleaseWait(true);
|
|
33
33
|
const { greetingInput } = e.target.elements;
|
|
34
|
-
|
|
35
|
-
.then(async () => {return
|
|
34
|
+
helloNEAR.setGreeting(greetingInput.value)
|
|
35
|
+
.then(async () => {return helloNEAR.getGreeting();})
|
|
36
36
|
.then(setValueFromBlockchain)
|
|
37
37
|
.finally(() => {
|
|
38
38
|
setUiPleaseWait(false);
|
|
@@ -44,7 +44,7 @@ export default function App({ isSignedIn, contract, wallet }) {
|
|
|
44
44
|
<SignOutButton accountId={wallet.accountId} onClick={() => wallet.signOut()}/>
|
|
45
45
|
<main className={uiPleaseWait ? 'please-wait' : ''}>
|
|
46
46
|
<h1>
|
|
47
|
-
The
|
|
47
|
+
The helloNEAR says: <span className="greeting">{valueFromBlockchain}</span>
|
|
48
48
|
</h1>
|
|
49
49
|
<form onSubmit={changeGreeting} className="change">
|
|
50
50
|
<label>Change greeting:</label>
|
|
@@ -1,21 +1,25 @@
|
|
|
1
|
+
// React
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
+
import ReactDOM from 'react-dom';
|
|
3
4
|
import App from './App';
|
|
5
|
+
|
|
6
|
+
// NEAR
|
|
7
|
+
import { HelloNEAR } from './near-interface';
|
|
4
8
|
import { Wallet } from './near-wallet';
|
|
5
|
-
import { Contract } from './near-interface';
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
// When creating the wallet you can optionally ask to create an access key
|
|
11
|
+
// Having the key enables to call non-payable methods without interrupting the user to sign
|
|
12
|
+
const wallet = new Wallet({ createAccessKeyFor: process.env.CONTRACT_NAME })
|
|
8
13
|
|
|
9
|
-
//
|
|
10
|
-
const
|
|
11
|
-
const wallet = new Wallet({contractId: contractId});
|
|
12
|
-
const contract = new Contract({wallet: wallet});
|
|
14
|
+
// Abstract the logic of interacting with the contract to simplify your flow
|
|
15
|
+
const helloNEAR = new HelloNEAR({ contractId: process.env.CONTRACT_NAME, walletToUse: wallet });
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
// Setup on page load
|
|
18
|
+
window.onload = async () => {
|
|
19
|
+
const isSignedIn = await wallet.startUp()
|
|
20
|
+
|
|
21
|
+
ReactDOM.render(
|
|
22
|
+
<App isSignedIn={isSignedIn} helloNEAR={helloNEAR} wallet={wallet} />,
|
|
23
|
+
document.getElementById('root')
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -43,7 +43,7 @@ export function EducationalText() {
|
|
|
43
43
|
</p>
|
|
44
44
|
<ol>
|
|
45
45
|
<li>
|
|
46
|
-
Look in <code>
|
|
46
|
+
Look in <code>frontend/App.js</code> - you'll see <code>getGreeting</code> and <code>setGreeting</code> being called on <code>contract</code>. What's this?
|
|
47
47
|
</li>
|
|
48
48
|
<li>
|
|
49
49
|
Ultimately, this <code>contract</code> code is defined in <code>./contract</code> – this is the source code for your <a target="_blank" rel="noreferrer" href="https://docs.near.org/docs/develop/contracts/overview">smart contract</a>.</li>
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
wallet;
|
|
1
|
+
/* Talking with a contract often involves transforming data, we recommend you to encapsulate that logic into a class */
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export class HelloNEAR {
|
|
4
|
+
constructor({ contractId, walletToUse }) {
|
|
5
|
+
this.contractId = contractId;
|
|
6
|
+
this.wallet = walletToUse;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
async getGreeting(){
|
|
9
|
-
return await this.wallet.viewMethod({method: 'get_greeting'});
|
|
9
|
+
async getGreeting() {
|
|
10
|
+
return await this.wallet.viewMethod({ contractId: this.contractId, method: 'get_greeting' });
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
async setGreeting(greeting){
|
|
13
|
-
return await this.wallet.callMethod({method: 'set_greeting', args:{message: greeting}});
|
|
12
|
+
|
|
13
|
+
async setGreeting(greeting) {
|
|
14
|
+
return await this.wallet.callMethod({ contractId: this.contractId, method: 'set_greeting', args: { message: greeting } });
|
|
14
15
|
}
|
|
15
16
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* A helper file that simplifies using the wallet selector */
|
|
2
|
+
|
|
1
3
|
// near api js
|
|
2
4
|
import { providers } from 'near-api-js';
|
|
3
5
|
|
|
@@ -19,28 +21,31 @@ const NO_DEPOSIT = '0';
|
|
|
19
21
|
export class Wallet {
|
|
20
22
|
walletSelector;
|
|
21
23
|
wallet;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
constructor({
|
|
26
|
-
|
|
24
|
+
network;
|
|
25
|
+
createAccessKeyFor;
|
|
26
|
+
|
|
27
|
+
constructor({ createAccessKeyFor = undefined, network = 'testnet' }) {
|
|
28
|
+
// Login to a wallet passing a contractId will create a local
|
|
29
|
+
// key, so the user skips signing non-payable transactions.
|
|
30
|
+
// Omitting the accountId will result in the user being
|
|
31
|
+
// asked to sign all transactions.
|
|
32
|
+
this.createAccessKeyFor = createAccessKeyFor
|
|
33
|
+
this.network = 'testnet'
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
// To be called when the website loads
|
|
30
37
|
async startUp() {
|
|
31
38
|
this.walletSelector = await setupWalletSelector({
|
|
32
|
-
network:
|
|
39
|
+
network: this.network,
|
|
33
40
|
modules: [setupMyNearWallet({ iconUrl: MyNearIconUrl }),
|
|
34
|
-
|
|
41
|
+
setupLedger({ iconUrl: LedgerIconUrl })],
|
|
35
42
|
});
|
|
36
43
|
|
|
37
44
|
const isSignedIn = this.walletSelector.isSignedIn();
|
|
38
45
|
|
|
39
46
|
if (isSignedIn) {
|
|
40
|
-
const { accounts } = this.walletSelector.store.getState();
|
|
41
|
-
|
|
42
47
|
this.wallet = await this.walletSelector.wallet();
|
|
43
|
-
this.accountId = accounts[0].accountId;
|
|
48
|
+
this.accountId = this.walletSelector.store.getState().accounts[0].accountId;
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
return isSignedIn;
|
|
@@ -49,19 +54,19 @@ export class Wallet {
|
|
|
49
54
|
// Sign-in method
|
|
50
55
|
signIn() {
|
|
51
56
|
const description = 'Please select a wallet to sign in.';
|
|
52
|
-
const modal = setupModal(this.walletSelector, { contractId: this.
|
|
57
|
+
const modal = setupModal(this.walletSelector, { contractId: this.createAccessKeyFor, description });
|
|
53
58
|
modal.show();
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
// Sign-out method
|
|
57
62
|
signOut() {
|
|
58
63
|
this.wallet.signOut();
|
|
59
|
-
this.wallet = this.accountId = this.
|
|
64
|
+
this.wallet = this.accountId = this.createAccessKeyFor = null;
|
|
60
65
|
window.location.replace(window.location.origin + window.location.pathname);
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
// Make a read-only call to retrieve information from the network
|
|
64
|
-
async viewMethod({ contractId
|
|
69
|
+
async viewMethod({ contractId, method, args = {} }) {
|
|
65
70
|
const { network } = this.walletSelector.options;
|
|
66
71
|
const provider = new providers.JsonRpcProvider({ url: network.nodeUrl });
|
|
67
72
|
|
|
@@ -76,12 +81,10 @@ export class Wallet {
|
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
// Call a method that changes the contract's state
|
|
79
|
-
async callMethod({ contractId
|
|
80
|
-
const { accountId } = this.walletSelector.store.getState().accounts[0];
|
|
81
|
-
|
|
84
|
+
async callMethod({ contractId, method, args = {}, gas = THIRTY_TGAS, deposit = NO_DEPOSIT }) {
|
|
82
85
|
// Sign a transaction with the "FunctionCall" action
|
|
83
86
|
return await this.wallet.signAndSendTransaction({
|
|
84
|
-
signerId: accountId,
|
|
87
|
+
signerId: this.accountId,
|
|
85
88
|
receiverId: contractId,
|
|
86
89
|
actions: [
|
|
87
90
|
{
|
|
@@ -60,18 +60,20 @@
|
|
|
60
60
|
</p>
|
|
61
61
|
<ol>
|
|
62
62
|
<li>
|
|
63
|
-
Look in <code
|
|
64
|
-
and <code>
|
|
63
|
+
Look in <code>index.js</code> and <code>near-api.js</code> – you'll see <code>get_greeting</code>
|
|
64
|
+
and <code>set_greeting</code> being called on <code>contract</code>.
|
|
65
65
|
What's this?
|
|
66
66
|
</li>
|
|
67
67
|
<li>
|
|
68
68
|
Ultimately, this <code>contract</code> code is defined in
|
|
69
|
-
<code
|
|
69
|
+
<code>assembly/index.ts</code> – this is the source code for your
|
|
70
70
|
<a target="_blank" href="https://docs.near.org/docs/develop/contracts/overview">smart contract</a>.
|
|
71
71
|
</li>
|
|
72
72
|
<li>
|
|
73
|
-
When you run <code>npm run dev</code>, the
|
|
74
|
-
to the NEAR testnet. You
|
|
73
|
+
When you run <code>npm run dev</code>, the code in
|
|
74
|
+
<code>assembly/index.ts</code> gets deployed to the NEAR testnet. You
|
|
75
|
+
can see how this happens by looking in <code>package.json</code> at the
|
|
76
|
+
<code>scripts</code> section to find the <code>dev</code> command.
|
|
75
77
|
</li>
|
|
76
78
|
</ol>
|
|
77
79
|
<hr>
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import 'regenerator-runtime/runtime';
|
|
2
|
-
import { Contract } from './near-interface';
|
|
3
2
|
import { Wallet } from './near-wallet';
|
|
3
|
+
import { HelloNEAR } from './near-interface';
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
const
|
|
5
|
+
// When creating the wallet you can optionally ask to create an access key
|
|
6
|
+
// Having the key enables to call non-payable methods without interrupting the user to sign
|
|
7
|
+
const wallet = new Wallet({ createAccessKeyFor: process.env.CONTRACT_NAME })
|
|
8
|
+
|
|
9
|
+
// Abstract the logic of interacting with the contract to simplify your flow
|
|
10
|
+
const helloNEAR = new HelloNEAR({ contractId: process.env.CONTRACT_NAME, walletToUse: wallet });
|
|
8
11
|
|
|
9
12
|
// Setup on page load
|
|
10
13
|
window.onload = async () => {
|
|
11
14
|
let isSignedIn = await wallet.startUp();
|
|
12
15
|
|
|
13
|
-
if(isSignedIn){
|
|
16
|
+
if (isSignedIn) {
|
|
14
17
|
signedInFlow();
|
|
15
|
-
}else{
|
|
18
|
+
} else {
|
|
16
19
|
signedOutFlow();
|
|
17
20
|
}
|
|
18
21
|
|
|
@@ -22,7 +25,7 @@ window.onload = async () => {
|
|
|
22
25
|
// Button clicks
|
|
23
26
|
document.querySelector('form').onsubmit = doUserAction;
|
|
24
27
|
document.querySelector('#sign-in-button').onclick = () => { wallet.signIn(); };
|
|
25
|
-
document.querySelector('#sign-out-button').onclick = () => { wallet.signOut(); };
|
|
28
|
+
document.querySelector('#sign-out-button').onclick = () => { wallet.signOut(); };
|
|
26
29
|
|
|
27
30
|
// Take the new greeting and send it to the contract
|
|
28
31
|
async function doUserAction(event) {
|
|
@@ -32,7 +35,7 @@ async function doUserAction(event) {
|
|
|
32
35
|
document.querySelector('#signed-in-flow main')
|
|
33
36
|
.classList.add('please-wait');
|
|
34
37
|
|
|
35
|
-
await
|
|
38
|
+
await helloNEAR.setGreeting(greeting.value);
|
|
36
39
|
|
|
37
40
|
// ===== Fetch the data from the blockchain =====
|
|
38
41
|
await fetchGreeting();
|
|
@@ -42,7 +45,7 @@ async function doUserAction(event) {
|
|
|
42
45
|
|
|
43
46
|
// Get greeting from the contract on chain
|
|
44
47
|
async function fetchGreeting() {
|
|
45
|
-
const currentGreeting = await
|
|
48
|
+
const currentGreeting = await helloNEAR.getGreeting();
|
|
46
49
|
|
|
47
50
|
document.querySelectorAll('[data-behavior=greeting]').forEach(el => {
|
|
48
51
|
el.innerText = currentGreeting;
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
"build": "parcel build index.html --public-url ./"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
|
+
"env-cmd": "^10.1.0",
|
|
11
|
+
"events": "^3.3.0",
|
|
10
12
|
"nodemon": "^2.0.16",
|
|
11
|
-
"parcel": "^2.
|
|
12
|
-
"process": "^0.11.10"
|
|
13
|
-
"env-cmd": "^10.1.0"
|
|
13
|
+
"parcel": "^2.7.0",
|
|
14
|
+
"process": "^0.11.10"
|
|
14
15
|
},
|
|
15
16
|
"dependencies": {
|
|
16
17
|
"@near-wallet-selector/core": "^7.0.0",
|
|
@@ -26,4 +27,4 @@
|
|
|
26
27
|
"@near-wallet-selector/wallet-connect": "^7.0.0",
|
|
27
28
|
"near-api-js": "^0.44.2"
|
|
28
29
|
}
|
|
29
|
-
}
|
|
30
|
+
}
|