diff --git a/next-env.d.ts b/next-env.d.ts
index 4f11a03..40c3d68 100644
--- a/next-env.d.ts
+++ b/next-env.d.ts
@@ -2,4 +2,4 @@
///
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/basic-features/typescript for more information.
+// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/package.json b/package.json
index abcb350..84b1129 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"homepage": "https://github.com/celo-org/celo-mondo",
"dependencies": {
"@celo/abis": "^11.0.0",
+ "@celo/compliance": "^1.0.23",
"@headlessui/react": "^1.7.18",
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6",
"@metamask/post-message-stream": "6.1.2",
diff --git a/src/app/app.tsx b/src/app/app.tsx
index 6d9d603..2d9c323 100644
--- a/src/app/app.tsx
+++ b/src/app/app.tsx
@@ -6,6 +6,7 @@ import 'react-toastify/dist/ReactToastify.css';
import { ErrorBoundary } from 'src/components/errors/ErrorBoundary';
import { Footer } from 'src/components/nav/Footer';
import { Header } from 'src/components/nav/Header';
+import { LegalRestrict } from 'src/components/police';
import { WagmiContext } from 'src/config/wagmi';
import { TransactionModal } from 'src/features/transactions/TransactionModal';
import { useIsSsr } from 'src/utils/ssr';
@@ -17,7 +18,9 @@ export function App({ children }: PropsWithChildren) {
- {children}
+
+ {children}
+
diff --git a/src/app/police/route.ts b/src/app/police/route.ts
new file mode 100644
index 0000000..982ec08
--- /dev/null
+++ b/src/app/police/route.ts
@@ -0,0 +1,35 @@
+import { headers } from 'next/headers';
+import { NextResponse, type NextRequest } from 'next/server';
+
+export function GET(request: NextRequest) {
+ const headerList = headers();
+
+ const country = request.geo?.country || (headerList.get('x-vercel-ip-country') as string);
+ const region = request.geo?.region || (headerList.get('x-vercel-ip-country-region') as string);
+
+ console.info('country', country, region);
+
+ if (isForbiddenLand(country, region)) {
+ return new NextResponse(null, { status: 451 });
+ }
+
+ return new NextResponse(null, { status: 202 });
+}
+const RESTRICTED_COUNTRIES = new Set(['KP', 'IR', 'CU', 'SY']);
+
+// https://www.iso.org/obp/ui/#iso:code:3166:UA although listed with UA prefix. the header/api recieved that and just used the number
+const crimea = '43';
+const luhansk = '09';
+const donetska = '14';
+//https://en.wikipedia.org/wiki/Russian-occupied_territories_of_Ukraine
+const RESTRICED_SUBREGION: Record> = {
+ UA: new Set([crimea, luhansk, donetska]),
+};
+
+function isForbiddenLand(iso3166Country: string, iso3166Region: string) {
+ const iso3166CountryUppercase = iso3166Country?.toUpperCase();
+ return (
+ RESTRICTED_COUNTRIES.has(iso3166CountryUppercase) ||
+ RESTRICED_SUBREGION[iso3166CountryUppercase]?.has(iso3166Region)
+ );
+}
diff --git a/src/components/police.tsx b/src/components/police.tsx
new file mode 100644
index 0000000..db76a54
--- /dev/null
+++ b/src/components/police.tsx
@@ -0,0 +1,55 @@
+import { OFAC_SANCTIONS_LIST_URL, SANCTIONED_ADDRESSES } from '@celo/compliance';
+import { PropsWithChildren, useEffect } from 'react';
+import { readFromCache, writeToCache } from 'src/utils/localSave';
+import { useAccount, useDisconnect } from 'wagmi';
+
+export function LegalRestrict(props: PropsWithChildren) {
+ usePolice();
+ return props.children;
+}
+
+function usePolice() {
+ const { address, isConnected } = useAccount();
+ const { disconnect } = useDisconnect();
+
+ useEffect(() => {
+ if (isConnected && address) {
+ isSanctionedAddress(address).then((isSanctioned) => {
+ if (isSanctioned) {
+ disconnect();
+ alert('The Address is under OFAC Sanctions');
+ }
+ });
+ fetch('/police').then((response) => {
+ if (response.status === 451) {
+ disconnect();
+ alert('The Region is under Sanction');
+ }
+ });
+ }
+ }, [isConnected, address, disconnect]);
+}
+
+const DAY = 24 * 60 * 60 * 1000;
+
+export async function isSanctionedAddress(address: string): Promise {
+ const cache = readFromCache(OFAC_SANCTIONS_LIST_URL);
+ if (cache && cache.ts + DAY > Date.now()) {
+ return cache.data.includes(address.toLowerCase());
+ }
+
+ const sanctionedAddresses: string[] = await fetch(OFAC_SANCTIONS_LIST_URL)
+ .then((x) => x.json())
+ .catch(() => SANCTIONED_ADDRESSES); // fallback if github is down or something.
+
+ if (process.env.NODE_ENV !== 'production' && process.env.TEST_SANCTIONED_ADDRESS) {
+ sanctionedAddresses.push(process.env.TEST_SANCTIONED_ADDRESS);
+ }
+
+ writeToCache(
+ OFAC_SANCTIONS_LIST_URL,
+ sanctionedAddresses.map((x) => x.toLowerCase()),
+ );
+
+ return isSanctionedAddress(address);
+}
diff --git a/src/config/proposals.json b/src/config/proposals.json
index fadd718..092e46c 100644
--- a/src/config/proposals.json
+++ b/src/config/proposals.json
@@ -1988,14 +1988,15 @@
"cgpUrlRaw": "https://raw.githubusercontent.com/celo-org/governance/main/CGPs/cgp-0132.md",
"title": "Proposal for Funding Stabila - Driving Stablecoin Adoption on Celo",
"author": "Kevin Tharayil (@KevinTharayil)",
- "stage": 5,
- "id": 173,
+ "stage": 6,
+ "id": 176,
"url": "https://forum.celo.org/t/final-proposal-for-funding-stabila-driving-stablecoin-adoption-on-celo/7810",
"timestamp": 1714089600000,
+ "timestampExecuted": 1715904000000,
"votes": {
- "yes": "13352572718004564022826739",
- "no": "199993080818103341960279",
- "abstain": "119152064836338536867928"
+ "yes": "13900081646743131204940936",
+ "no": "177123734397638981301695",
+ "abstain": "115470039848008913828587"
}
},
{
@@ -2219,5 +2220,15 @@
"id": 188,
"url": "https://forum.celo.org/t/launch-of-puso-the-philippines-first-community-led-stablecoin/8786/10?u=philbow61",
"timestamp": 1724889600000
+ },
+ {
+ "cgp": 149,
+ "cgpUrl": "https://github.com/celo-org/governance/blob/main/CGPs/cgp-0149.md",
+ "cgpUrlRaw": "https://raw.githubusercontent.com/celo-org/governance/main/CGPs/cgp-0149.md",
+ "title": "Decrease `constitution` parameter for `GoldToken.increaseAllowance`",
+ "author": "Martin Chrzanowski (@m-chrzan)",
+ "stage": 0,
+ "url": "https://forum.celo.org/t/decrease-constitution-parameter-for-goldtoken-increaseallowance/9002",
+ "timestamp": 1725926400000
}
]
\ No newline at end of file
diff --git a/src/utils/localSave.ts b/src/utils/localSave.ts
new file mode 100644
index 0000000..2ffbdca
--- /dev/null
+++ b/src/utils/localSave.ts
@@ -0,0 +1,12 @@
+export const writeToCache = (url: string, data: string[]) =>
+ localStorage.setItem(url, JSON.stringify({ ts: Date.now(), data }));
+
+export const deleteFromCache = (url: string) => localStorage.removeItem(url);
+
+export const readFromCache = (url: string) => {
+ const cached = localStorage.getItem(url);
+ if (cached) {
+ return JSON.parse(cached) as { ts: number; data: string[] };
+ }
+ return null;
+};
diff --git a/yarn.lock b/yarn.lock
index a37b782..5f0cb8d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -461,6 +461,7 @@ __metadata:
resolution: "@celo-mondo/celo-mondo-web@workspace:."
dependencies:
"@celo/abis": "npm:^11.0.0"
+ "@celo/compliance": "npm:^1.0.23"
"@headlessui/react": "npm:^1.7.18"
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6"
"@metamask/post-message-stream": "npm:6.1.2"
@@ -521,6 +522,13 @@ __metadata:
languageName: node
linkType: hard
+"@celo/compliance@npm:^1.0.23":
+ version: 1.0.23
+ resolution: "@celo/compliance@npm:1.0.23"
+ checksum: 10/3b67363e4fd266d03fdecfd3dd6ad17b0328c235d79522eea6c815864d7be5fd0aba6accb2b63dd78cc2ec2ec07ed8c85de8a282b8de60c9bd1cd72ff09c01bd
+ languageName: node
+ linkType: hard
+
"@coinbase/wallet-sdk@npm:4.0.4":
version: 4.0.4
resolution: "@coinbase/wallet-sdk@npm:4.0.4"