Skip to content

Commit 4388cac

Browse files
authored
Merge pull request #28 from near-examples/updatePackage
feat(ui): update packages and improve UX/UI
2 parents 99c04f7 + 8430687 commit 4388cac

File tree

7 files changed

+209
-54
lines changed

7 files changed

+209
-54
lines changed

contract-rs/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ crate-type = ["cdylib", "rlib"]
1313

1414
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1515
[dependencies]
16-
near-sdk = { version = "5.1.0", features = ["legacy"] }
16+
near-sdk = { version = "5.7.0", features = ["legacy"] }
1717

1818
[dev-dependencies]
19-
near-sdk = { version = "5.1.0", features = ["unit-testing"] }
20-
near-workspaces = { version = "0.10.0", features = ["unstable"] }
19+
near-sdk = { version = "5.7.0", features = ["unit-testing"] }
20+
near-workspaces = { version = "0.16.0", features = ["unstable"] }
2121
tokio = { version = "1.12.0", features = ["full"] }
2222
serde_json = "1"
2323

frontend/package.json

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,29 @@
1212
"lint": "next lint"
1313
},
1414
"dependencies": {
15-
"@near-wallet-selector/core": "^8.9.11",
16-
"@near-wallet-selector/here-wallet": "^8.9.11",
17-
"@near-wallet-selector/modal-ui": "^8.9.11",
18-
"@near-wallet-selector/my-near-wallet": "^8.9.11",
15+
"@near-js/providers": "^1.0.1",
16+
"@near-wallet-selector/bitte-wallet": "^8.9.14",
17+
"@near-wallet-selector/core": "^8.9.14",
18+
"@near-wallet-selector/ethereum-wallets": "^8.9.14",
19+
"@near-wallet-selector/here-wallet": "^8.9.14",
20+
"@near-wallet-selector/ledger": "^8.9.14",
21+
"@near-wallet-selector/meteor-wallet": "^8.9.14",
22+
"@near-wallet-selector/modal-ui": "^8.9.14",
23+
"@near-wallet-selector/my-near-wallet": "^8.9.14",
24+
"@near-wallet-selector/near-mobile-wallet": "^8.9.14",
25+
"@near-wallet-selector/sender": "^8.9.14",
26+
"@near-wallet-selector/welldone-wallet": "^8.9.14",
27+
"@web3modal/wagmi": "^5.1.11",
1928
"bootstrap": "^5",
2029
"bootstrap-icons": "^1.11.3",
2130
"near-api-js": "^4.0.3",
22-
"next": "14.2.3",
31+
"next": "14.2.5",
2332
"react": "^18",
24-
"react-dom": "^18"
33+
"react-dom": "^18",
34+
"wagmi": "^2.13.3"
2535
},
26-
"resolutions": {
27-
"near-api-js": "4.0.3"
28-
},
2936
"devDependencies": {
37+
"encoding": "^0.1.13",
3038
"eslint": "^8",
3139
"eslint-config-next": "14.2.3"
3240
}

frontend/src/config.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,23 @@ const contractPerNetwork = {
44

55
export const NetworkId = "testnet";
66
export const CoinFlipContract = contractPerNetwork[NetworkId];
7+
8+
// Chains for EVM Wallets
9+
const evmWalletChains = {
10+
mainnet: {
11+
chainId: 397,
12+
name: 'Near Mainnet',
13+
explorer: 'https://eth-explorer.near.org',
14+
rpc: 'https://eth-rpc.mainnet.near.org',
15+
},
16+
testnet: {
17+
chainId: 398,
18+
name: 'Near Testnet',
19+
explorer: 'https://eth-explorer-testnet.near.org',
20+
rpc: 'https://eth-rpc.testnet.near.org',
21+
},
22+
};
23+
24+
25+
export const EVMWalletChain = evmWalletChains[NetworkId];
26+

frontend/src/pages/_app.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import { Navigation } from '@/components/Navigation';
77
import { Wallet } from '@/wallets/near';
88
import { NetworkId, CoinFlipContract } from '@/config';
99

10-
const wallet = new Wallet({ createAccessKeyFor: CoinFlipContract, networkId: NetworkId });
10+
// Create an access key so the user does not need to sign transactions. Read more about access keys here: https://docs.near.org/concepts/protocol/access-keys
11+
const wallet = new Wallet({
12+
createAccessKeyFor: CoinFlipContract,
13+
networkId: NetworkId,
14+
});
15+
1116

1217
export default function MyApp({ Component, pageProps }) {
1318
const [signedAccountId, setSignedAccountId] = useState('');

frontend/src/pages/index.js

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ export default function Home() {
1313
const [choice, setChoice] = useState();
1414

1515
useEffect(() => {
16-
if (signedAccountId) updateScore();
16+
if (!signedAccountId) return;
17+
18+
wallet.viewMethod({
19+
contractId: CoinFlipContract,
20+
method: "points_of",
21+
args: { player: signedAccountId },
22+
}).then((score) => setPoints(score))
23+
1724
}, [signedAccountId]);
1825

1926
const handleChoice = async (guess) => {
@@ -32,22 +39,11 @@ export default function Home() {
3239

3340
if (guess === outcome) {
3441
setStatus("You were right, you won a point!");
42+
setPoints(points + 1);
3543
} else {
3644
setStatus("You were wrong, you lost a point");
45+
setPoints(points ? points - 1 : 0);
3746
}
38-
39-
updateScore();
40-
};
41-
42-
const updateScore = async () => {
43-
44-
const score = await wallet.viewMethod({
45-
contractId: CoinFlipContract,
46-
method: "points_of",
47-
args: { player: signedAccountId },
48-
});
49-
50-
setPoints(score);
5147
};
5248

5349
let color = choice === side ? "btn-success" : "btn-danger";
@@ -68,17 +64,15 @@ export default function Home() {
6864
</h2>
6965
<div className="d-flex justify-content-center">
7066
<button
71-
className={`btn me-2 ${
72-
choice === "heads" && side !== 'loading' ? color : "btn-primary"
73-
}`}
67+
className={`btn me-2 ${choice === "heads" && side !== 'loading' ? color : "btn-primary"
68+
}`}
7469
onClick={() => handleChoice("heads")}
7570
>
7671
Heads
7772
</button>
7873
<button
79-
className={`btn ${
80-
choice === "tails" && side !== 'loading' ? color : "btn-primary"
81-
}`}
74+
className={`btn ${choice === "tails" && side !== 'loading' ? color : "btn-primary"
75+
}`}
8276
onClick={() => handleChoice("tails")}
8377
>
8478
Tails

frontend/src/wallets/near.js

Lines changed: 107 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1-
// near api js
2-
import { providers } from 'near-api-js';
3-
41
// wallet selector
5-
import { distinctUntilChanged, map } from 'rxjs';
62
import '@near-wallet-selector/modal-ui/styles.css';
7-
import { setupModal } from '@near-wallet-selector/modal-ui';
3+
4+
import { setupBitteWallet } from '@near-wallet-selector/bitte-wallet';
85
import { setupWalletSelector } from '@near-wallet-selector/core';
9-
import { setupHereWallet } from '@near-wallet-selector/here-wallet';
6+
import { setupEthereumWallets } from '@near-wallet-selector/ethereum-wallets';
7+
import { setupLedger } from '@near-wallet-selector/ledger';
8+
import { setupMeteorWallet } from '@near-wallet-selector/meteor-wallet';
9+
import { setupModal } from '@near-wallet-selector/modal-ui';
1010
import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet';
11+
import { setupSender } from '@near-wallet-selector/sender';
12+
import { setupHereWallet } from '@near-wallet-selector/here-wallet';
13+
import { setupNearMobileWallet } from '@near-wallet-selector/near-mobile-wallet';
14+
import { setupWelldoneWallet } from '@near-wallet-selector/welldone-wallet';
15+
16+
17+
// near api js
18+
import { providers, utils } from 'near-api-js';
19+
import { createContext } from 'react';
20+
21+
// ethereum wallets
22+
import { wagmiConfig, web3Modal } from '@/wallets/web3modal';
1123

1224
const THIRTY_TGAS = '30000000000000';
1325
const NO_DEPOSIT = '0';
@@ -30,27 +42,32 @@ export class Wallet {
3042
/**
3143
* To be called when the website loads
3244
* @param {Function} accountChangeHook - a function that is called when the user signs in or out#
33-
* @returns {Promise<string>} - the accountId of the signed-in user
45+
* @returns {Promise<string>} - the accountId of the signed-in user
3446
*/
3547
startUp = async (accountChangeHook) => {
3648
this.selector = setupWalletSelector({
3749
network: this.networkId,
38-
modules: [setupMyNearWallet(), setupHereWallet()]
50+
modules: [
51+
setupMeteorWallet(),
52+
setupEthereumWallets({ wagmiConfig, web3Modal, alwaysOnboardDuringSignIn: true }),
53+
setupLedger(),
54+
setupBitteWallet(),
55+
setupHereWallet(),
56+
setupSender(),
57+
setupNearMobileWallet(),
58+
setupWelldoneWallet(),
59+
setupMyNearWallet(),
60+
],
3961
});
4062

4163
const walletSelector = await this.selector;
4264
const isSignedIn = walletSelector.isSignedIn();
4365
const accountId = isSignedIn ? walletSelector.store.getState().accounts[0].accountId : '';
4466

45-
walletSelector.store.observable
46-
.pipe(
47-
map(state => state.accounts),
48-
distinctUntilChanged()
49-
)
50-
.subscribe(accounts => {
51-
const signedAccount = accounts.find((account) => account.active)?.accountId;
52-
accountChangeHook(signedAccount);
53-
});
67+
walletSelector.store.observable.subscribe(async (state) => {
68+
const signedAccount = state?.accounts.find((account) => account.active)?.accountId;
69+
accountChangeHook(signedAccount || '');
70+
});
5471

5572
return accountId;
5673
};
@@ -83,7 +100,7 @@ export class Wallet {
83100
const url = `https://rpc.${this.networkId}.near.org`;
84101
const provider = new providers.JsonRpcProvider({ url });
85102

86-
let res = await provider.query({
103+
const res = await provider.query({
87104
request_type: 'call_function',
88105
account_id: contractId,
89106
method_name: method,
@@ -93,7 +110,6 @@ export class Wallet {
93110
return JSON.parse(Buffer.from(res.result).toString());
94111
};
95112

96-
97113
/**
98114
* Makes a call to a contract
99115
* @param {Object} options - the options for the call
@@ -126,7 +142,7 @@ export class Wallet {
126142
};
127143

128144
/**
129-
* Retrieves transaction result from the network
145+
* Makes a call to a contract
130146
* @param {string} txhash - the transaction hash
131147
* @returns {Promise<JSON.value>} - the result of the transaction
132148
*/
@@ -135,7 +151,77 @@ export class Wallet {
135151
const { network } = walletSelector.options;
136152
const provider = new providers.JsonRpcProvider({ url: network.nodeUrl });
137153

138-
const transaction = await provider.txStatus(txhash, 'unnused');
154+
// Retrieve transaction result from the network
155+
const transaction = await provider.txStatus(txhash, 'unused');
139156
return providers.getTransactionLastResult(transaction);
140157
};
158+
159+
/**
160+
* Gets the balance of an account
161+
* @param {string} accountId - the account id to get the balance of
162+
* @param {boolean} format - whether to format the balance
163+
* @returns {Promise<number>} - the balance of the account
164+
*
165+
*/
166+
getBalance = async (accountId, format = false) => {
167+
const walletSelector = await this.selector;
168+
const { network } = walletSelector.options;
169+
const provider = new providers.JsonRpcProvider({ url: network.nodeUrl });
170+
171+
// Retrieve account state from the network
172+
const account = await provider.query({
173+
request_type: 'view_account',
174+
account_id: accountId,
175+
finality: 'final',
176+
});
177+
178+
// Format the amount if needed
179+
if (format) {
180+
return account.amount ? utils.format.formatNearAmount(account.amount) : '0';
181+
} else {
182+
return account.amount || '0';
183+
}
184+
};
185+
186+
/**
187+
* Signs and sends transactions
188+
* @param {Object[]} transactions - the transactions to sign and send
189+
* @returns {Promise<Transaction[]>} - the resulting transactions
190+
*
191+
*/
192+
signAndSendTransactions = async ({ transactions }) => {
193+
const selectedWallet = await (await this.selector).wallet();
194+
return selectedWallet.signAndSendTransactions({ transactions });
195+
};
196+
197+
/**
198+
*
199+
* @param {string} accountId
200+
* @returns {Promise<Object[]>} - the access keys for the
201+
*/
202+
getAccessKeys = async (accountId) => {
203+
const walletSelector = await this.selector;
204+
const { network } = walletSelector.options;
205+
const provider = new providers.JsonRpcProvider({ url: network.nodeUrl });
206+
207+
// Retrieve account state from the network
208+
const keys = await provider.query({
209+
request_type: 'view_access_key_list',
210+
account_id: accountId,
211+
finality: 'final',
212+
});
213+
return keys.keys;
214+
};
141215
}
216+
217+
/**
218+
* @typedef NearContext
219+
* @property {import('./wallets/near').Wallet} wallet Current wallet
220+
* @property {string} signedAccountId The AccountId of the signed user
221+
*/
222+
223+
/** @type {import ('react').Context<NearContext>} */
224+
export const NearContext = createContext({
225+
wallet: undefined,
226+
signedAccountId: '',
227+
});

frontend/src/wallets/web3modal.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { injected,walletConnect } from '@wagmi/connectors';
2+
import { createConfig,http, reconnect } from '@wagmi/core';
3+
import { createWeb3Modal } from '@web3modal/wagmi';
4+
5+
import { EVMWalletChain,NetworkId } from '@/config';
6+
7+
// Config
8+
const near = {
9+
id: EVMWalletChain.chainId,
10+
name: EVMWalletChain.name,
11+
nativeCurrency: {
12+
decimals: 18,
13+
name: 'NEAR',
14+
symbol: 'NEAR',
15+
},
16+
rpcUrls: {
17+
default: { http: [EVMWalletChain.rpc] },
18+
public: { http: [EVMWalletChain.rpc] },
19+
},
20+
blockExplorers: {
21+
default: {
22+
name: 'NEAR Explorer',
23+
url: EVMWalletChain.explorer,
24+
},
25+
},
26+
testnet: NetworkId === 'testnet',
27+
};
28+
29+
// Get your projectId at https://cloud.reown.com
30+
const projectId = '5bb0fe33763b3bea40b8d69e4269b4ae';
31+
32+
export const wagmiConfig = createConfig({
33+
chains: [near],
34+
transports: { [near.id]: http() },
35+
connectors: [walletConnect({ projectId, showQrModal: false }), injected({ shimDisconnect: true })],
36+
});
37+
38+
// Preserve login state on page reload
39+
reconnect(wagmiConfig);
40+
41+
// Modal for login
42+
export const web3Modal = createWeb3Modal({ wagmiConfig, projectId });

0 commit comments

Comments
 (0)