KiemTienOnline360

Chia sẻ hành trình kiếm tiền online bắt đầu từ con số 0

Kiến thức lập trình, Kiến thức phần mềm, Kỹ nghệ phần mềm

Hướng dẫn triển khai Pancake Swap v1 trên môi trường Binance Smart Chain BSC Testnet

Hướng dẫn triển khai Pancake Swap v1 trên môi trường Binance Smart Chain BSC Testnet

Hướng dẫn triển khai Pancake Swap v1 trên môi trường Binance Smart Chain BSC Testnet

Chia sẻ bài viết
5
(169)

Pancake Swap đang là nền tảng AMM mạnh nhất trên Binance Smart Chain, do đó nhu cầu các ứng dụng cần kết nối tới Pancake Swap khá nhiều. Nhưng có 1 điều hơi đáng tiếc là Pancake Swap không có môi trường Testnet để giúp các Developer dễ dàng hơn trong việc phát triển. Vì vậy mình đã triển khai phiên bản Pancake Swap trên môi trường Testnet, các Developers có thể tham khảo: https://pancake.kiemtienonline360.com/. Tính đến thời điểm hiện tại cũng đã có trên 105K transaction được thực hiện.

Môi trường Pancake Testnet hiện đang được khá nhiều DEV sử dụng, nhưng bên cạnh đó cũng có nhiều DEV mong muốn triển khai một phiên bản Pancake Swap cho riêng mình. Mình có đã nhận được khá nhiều yêu cầu hỗ trợ, giúp đỡ để triển khai phiên bản swap riêng, mà đôi khi mình không có nhiều thời gian để hỗ trợ hết các bạn. Vì thế mình viết bài này mục đích hướng dẫn chi tiết cho các bạn các thức triển khai một phiên bản Pancake Swap trên môi trường Testnet.

Toàn bộ source code mình đã đẩy lên Github tại địa chỉ: https://github.com/kiemtienonline360/pancake-swap-v1-bsc-testnet. Đây là source code của Pancake Swap v1, phiên bản v2 cũng không khác nhiều, chủ yếu là thay đổi phần trăm chia sẻ về phí. Tôi tin rằng nếu bạn biết cách triển khai phiên bản v1 thì phiên bản v2 cũng sẽ không làm khó được bạn.

Để triển khai một phiên bản Pancake Swap đầy đủ, bạn phải thực hiện 3 bước như sau:

  • Bước 1: Triển khai contract PancakeFactory
  • Bước 2: Triển khai contract PancakeRouter
  • Bước 3: Triển khai giao diện PancakeSwap

Trong 3 bước trên thì Bước 3 là mất nhiều thời gian của mình nhất, Bước 2 có một chú ý nhỏ. Với hướng dẫn này, bạn chỉ mất 10 phút để triển khai toàn bộ Pancake Swap.

Trước khi bắt đầu, bạn cần thực hiện:

Bước 1: Triển khai contract PancakeFactory

Ở bước này bạn có thể sử dụng Truffle hoặc Remix để build và triển khai contract. Với nhưng người mới tiếp xúc thì đơn giản nhất là sử dụng Remix.

Các bước thực hiện như sau:

1.1 Cài đặt các thư viện cần thiết

Bạn vào thư mục “pancake-swap-core-v1” và đánh lệnh sau để cài đặt:

cd pancake-swap-core-v1
npm install

1.2 Gộp các contract thành 1 file contract

Vì project chia làm nhiều tệp .sol nhỏ nên không tiện cho việc sử dụng trên Remix, vì thế mình làm tool nhỏ để join các tệp này thành 1 tệp. Để join bạn đánh lệnh sau:

mkdir build
node tools/merge-contract.js

Bạn sẽ nhìn thấy tệp “PancakeFactory.sol.merge.txt” trong thư mục build.

1.3 Triển khai trên Remix

Bây giờ bạn vào trang Remix (https://remix.ethereum.org). Trên giao diện Remix, tạo contract mới tên PancakeFactory.sol, sau đó copy toàn bộ nội dung từ tệp PancakeFactory.sol.merge.txt sang PancakeFactory.sol

Sau đó, kích vào icon “Solidity complier” ở phía bên trái, chọn “0.5.16” trong mục COMPILER. Sau đó kích vào nút “Complile PancakeFactory.sol” để biên dịch. Bạn sẽ thấy thông báo dịch thành công và không có lỗi.

Biên dịch PancakeFactory thành công
Biên dịch PancakeFactory thành công

Sau đó, kích vào icon “Deploy & run transactions“, chọn “Injected Web3” trong mục in ENVIRONMENT, Remix sẽ yêu cầu kết nối với ví trên Metamask. Tiếp theo bạn chọn “PancakeFactory – …” trong mục CONTRACT, rồi nhập một địa chỉ ví bất kỳ của bạn trong trường “address_feeToSetter“. Cuối cùng kích nút “Deploy” và chờ.

Deploy PancakeFactory
Deploy PancakeFactory

Một khi triển khai xong, trong mục “Deployed Contracts“, bạn sẽ nhìn thấy contract mà bạn vừa triển khai, bạn sẽ lấy được địa chỉ của contract PancakeFactory. Mở rộng mục này bạn sẽ lấy được INIT_CODE_PAIR_HASH. Bạn cần lưu hai thông tin này lại để sử dụng trong Bước 2.

Thông tin PancakeFactory sau khi triển khai thành công
Thông tin PancakeFactory sau khi triển khai thành công

Dưới đây là thông tin contract mình đã triển khai cho hệ thống Test

Ghi chú: Thỉnh thoảng bạn có thể gặp lỗi khi tạo tệp .SOL mới trên trang https://remix.ethereum.org, thì bạn có thể sử dụng link sau (Dùng HTTP, không dùng HTTPS): http://remix.ethereum.org

Bước 2: Triển khai contract PancakeRouter

Thao tác về cơ bản cũng giống như trong Bước 1, nhưng bạn cần sửa một chút code.

Các bước thực hiện như sau:

2.1 Cài đặt các thư viện cần thiết

Bạn vào thư mục “pancake-swap-router-v1” và đánh lệnh sau để cài đặt:

cd pancake-swap-router-v1
npm install

2.2 Sửa một chút mã nguồn

Mở tệp “contracts/libraries/PancakeLibrary.sol“, đến dòng 25 (Trong hàm pairFor()), đổi xâu hexa tới giá trị INIT_CODE_PAIR_HASH của PancakeFactory mà bạn có trong Bước 1. Sau khi hoàn thành thì lưu lại.

Cập nhật  INIT_CODE_PAIR_HASH
Cập nhật INIT_CODE_PAIR_HASH

2.3 Gộp các contract thành 1 file contract

Để join các tệp .SOL thành 1 bạn đánh lệnh sau:

mkdir build
node tools/merge-contract.js

Bạn sẽ nhìn thấy tệp “PancakeRouter01.sol.merge.txt” trong thư mục build.

2.4 Triển khai trên Remix

Bây giờ bạn vào trang Remix (https://remix.ethereum.org). Trên giao diện Remix, tạo contract mới tên PancakeRouter.sol, sau đó copy toàn bộ nội dung từ tệp PancakeRouter01.sol.merge.txt sang PancakeRouter.sol

Sau đó, kích vào icon “Solidity complier” ở phía bên trái, chọn “0.6.6” trong mục COMPILER, chọn “Enable optimization” in mục COMPILER CONFIGURATION. Sau đó kích vào nút “Complile PancakeRouter.sol” để biên dịch. Bạn sẽ thấy thông báo dịch thành công và không có lỗi.

Biên dịch thành công PancakeRouter
Biên dịch thành công PancakeRouter

Kích vào icon “Deploy & run transactions“, chọn “Injected Web3” trong mục in ENVIRONMENT, Remix sẽ yêu cầu kết nối với ví trên Metamask. Tiếp theo bạn chọn “PancakeRouter01 – …” trong mục CONTRACT. Sau đó nhập địa chỉ contract PancakeFactory (Trong Bước 1) và địa chỉ WBNB 0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd. Đây là hai tham số để khởi tạo PancakeRouter. Cuối cùng kích nút “Deploy” và chờ.

Triển khai PancakeRouter
Triển khai PancakeRouter

Một khi triển khai xong, trong mục “Deployed Contracts“, bạn sẽ nhìn thấy contract mà bạn vừa triển khai, bạn sẽ lấy được địa chỉ của contract PancakeRouter. Bạn lưu địa chỉ này lại để sử dụng trong Bước 3.

Triển khai thành công
Triển khai thành công

Dưới đây là thông tin contract mình đã triển khai cho hệ thống Test:

Note: Bạn có thể sử dụng địa chỉ WBNB ở trên hoặc bạn có thể tự tạo WBNB mới.

Bước 3: Triển khai giao diện PancakeSwap

Trong các bước triển khai thì bước này là phức tạp nhất vì có nhiều phần sửa đổi. Các bước thực hiện như sau:

3.1 Cài đặt các thư viện cần thiết

Bạn vào thư mục “pancake-swap-interface-v1” và đánh lệnh sau để cài đặt:

cd pancake-swap-interface-v1
npm install
npm install --global yarn

Sau đó bạn copy tệp .env.development thành .env

3.2 Chỉnh sửa source code của PancakeSwap

Có khá nhiều chỗ cần chỉnh sửa, mình liệt kê lại như dưới, không cần thiết phải theo thứ tự:

  • Vào tệp “src/constants/token/pancakeswap.json” để thêm hoặc sửa các token được hỗ trợ mặc định trên Testnet. Token trên BSC Testnet bạn có thể tự triển khai, mã nguồn token thì có thể tham khảo: USDT, ETH, BUSD, DAI. Bạn nhớ thêm ảnh cho các token mới của bạn ở trong thư mục “public/images/coins” với tên là địa chỉ token ở dạng chữ thường.
  • Vào tệp “src/constants/index.ts“, cập nhật giá trị cho ROUTER_ADDRESS là địa chỉ PancakeRouter đã triển khai trong Bước 2. Trong tệp này, bạn cũng sửa thêm các token hiển thị mặc định.
  • Tôi đã sửa supportedChainIdsBscConnector trong tệp “src/connectors/index.ts“để chỉ hỗ trợ BSC Testnet. Bạn không cần thay đổi tệp này trừ khi bạn muốn triển khai trên network khác.
  • Hiện tại menu tôi đã comment lại gần hết, nếu bạn muốn thay đổi menu hiển thị bạn có thể sửa trong tệp “src/components/Menu/config.ts“.
  • Mở 3 tệp “node_modules/@pancakeswap-libs/sdk/dist/constants.d.ts“, “node_modules/@pancakeswap-libs/sdk/dist/sdk.cjs.development.js” và “node_modules/@pancakeswap-libs/sdk/dist/sdk.cjs.production.min.js“, cập nhật lại giá trị cho hai biến: FACTORY_ADDRESS và INIT_CODE_HASH => Do thư viện đang fix địa chỉ chứ không cho cấu hình.
  • Nếu WBNB bạn sử dụng không phải địa chỉ 0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd thì bạn nên cập nhật địa chỉ WBNB mới trong hai tệp “node_modules/@pancakeswap-libs/sdk/dist/sdk.cjs.development.js” và “node_modules/@pancakeswap-libs/sdk/dist/sdk.cjs.production.min.js“.

3.3 Chạy test ở local

Bạn chạy lệnh sau để test ở local:

yarn start

Một số lỗi phát sinh:

  • TypeScript error in pancake-swap-interface-v1/src/state/index.ts(43,3): Type ‘MergedState’ is not assignable to type
    Lỗi này phát sinh do phiên bản mới của thư viện “@reduxjs/toolkit” không tương thích. Bạn phải xóa phiên bản hiện tại và cài đặt chính xác phiên bản 1.3.5:
    npm remove @reduxjs/toolkit
    npm install @reduxjs/toolkit@1.3.5

3.4 Triển khai lên Github

Bạn mở tệp “.env.production” để sửa lại cấu hình cho phù hợp, đặc biệt là cấu hình PUBLIC_URL. Sau đó bạn đánh lệnh sau để build:

yarn build

Sau đó bạn để toàn bộ các tệp trong thư mục build lên github hoặc host là chạy được.

Tham khảo thêm:

Bài viết này có hữu ích với bạn?

Kích vào một biểu tượng ngôi sao để đánh giá bài viết!

Xếp hạng trung bình 5 / 5. Số phiếu: 169

Bài viết chưa có đánh giá! Hãy là người đầu tiên đánh giá bài viết này.

43 Bình luận

  1. hoangho

    Cannot compile interface with error log:

    Type ‘MergedState’ is not assignable to type ‘{ application?: { blockNumber: { [x: number]: number | undefined; }; popupList: { key: string; show: boolean; content: { txn: { hash: string; success: boolean; summary?: string | undefined; }; } | { listUpdate: { …; }; }; removeAfterMs: number | null; }[]; walletModalOpen: boolean; settingsMenuOpen: boolean; } | u…’.
    Types of property ‘user’ are incompatible.
    Type ‘{ [key: string]: any; }’ is missing the following properties from type ‘{ lastUpdateVersionTimestamp?: number | undefined; userDarkMode: boolean | null; matchesDarkMode: boolean; userExpertMode: boolean; userSlippageTolerance: number; userDeadline: number; tokens: { …; }; pairs: { …; }; timestamp: number; audioPlay: boolean; }’: userDarkMode, matchesDarkMode, userExpertMode, userSlippageTolerance, and 5 more. TS2322

    41 | },
    42 | middleware: […getDefaultMiddleware({ thunk: false }), save({ states: PERSISTED_KEYS })],
    > 43 | preloadedState: loadedState,
    | ^
    44 | })
    45 |
    46 | store.dispatch(updateVersion())

  2. Chu Linh One1Time

    Cái Liqidity prodider fee trong đó có 0.03% trả cho pancake treasure là nó đi về địa chỉ ví hay contract nào ạ?
    “For each trade a 0.2% fee is paid. 0.17% goes to liquidity providers and 0.03% goes to the PancakeSwap treasury.”

    • Câu hỏi của bạn rất hay. Tối qua mình mới có thời gian để tìm hiểu về vấn đề này. Mình xin trả lời bạn như sau:

      1. Trong phiên bản 1, Pancake sẽ thu 0.2% phí cho mỗi trade. Bạn sẽ nhìn thấy đoạn này ở hàm getAmountIn()getAmountOut() trong tệp PancakeLibrary.sol của Pancake.
        Pancake thu phí 0.2% cho mỗi trade
      2. Tiếp theo bạn xem hàm swap() trong tệp PancakePair.sol, bạn sẽ thấy toàn bộ phí này vẫn ở trong Liquidity Pool. Theo công thức của AMM thì hằng số K là không đổi sau mỗi trade nhưng do phí này mà sau mỗi trade số K có xu hướng tăng thêm 1 chút.Fee được cập nhật vào Liquidity Pool
      3. Bây giờ ta quay lại PancakeRouter.sol, xem hàm addLiquidity()addLiquidityETH(), ở cuối mỗi hàm này, hàm mint() của PancakePair sẽ được gọi để đúc ra LP Token trả cho người cung cấp thanh khoản. Trong hàm mint() này sẽ gọi hàm _mintFee(), đây chính là hàm có nhiệm vụ lấy 0.03% phí cho vào Pancake Treasury. Pancake Treasury chính là địa chỉ lưu trong biến feeTo của PancakeFactory.sol. Địa chỉ này do Owner thiết lập bằng cách gọi hàm setFeeTo(), địa chỉ này có thể là địa chỉ ví bình thường hoặc địa chỉ của 1 contract tùy vào nghiệp vụ thực tế.0.03% phí được đẩy vào PancakeSwap Treasury
  3. MK

    Hi bạn, mình muốn nhờ bạn hỗ trợ mình tạo ứng dụng này được ko?
    Mình sẽ trả phí cho bạn, vì mình ko rành về Nodejs, bạn gửi ib cho mình giúp mình bạn nhé
    https://www.fb.com/yone.linh/
    Skype: kiemtien_mk
    Bạn inbox giúp mình nhé, vì trên web bạn mình ko thấy contact, thank bạn

  4. Butiya Mahesh

    Hi,
    I just try to fork your pancakeswap version with your all raouter and factory address, but it will not shows the liquidity pool after creating pool, why?, please help me. all the changes you suggest was already done but still not working

  5. Hoàng

    Mình làm tới bước hợp đồng router thì báo lỗi chỗ địa chỉ hex như hình: https://uphinh.vn/image/dRMLK

    Không biết nó bị lỗi gì ạ ?

    • Nhìn ảnh thì mình thấy code bạn bị thiếu nhé. Library ở trên thiếu dấu “}” nên báo lỗi là đúng rùi bạn.
      Mình thấy code chưa chưa chuẩn, đúng ra đoạn “interface IPancakeRouter01” ở dòng khoảng 214 nhé.

  6. Luan

    mình chạy bước đầu pancake-swap-core-v1
    không thấy node_modules xuất hiện trong thư mục dự án
    khi mình node tools/merge-contract.js thì nó báo Error: Cannot find module ‘sol-merger’
    nên khi vào thư mục build thì không thấy có file nào cả

    • Do chưa cài đặt thư viện bạn nhé. Bạn vào thư mục pancake-swap-core-v1, rồi đánh lệnh sau để cài đặt:
      npm install
      (Yêu cầu bạn phải cài NodeJs rùi nhé)

  7. Jack Huynh

    Tôi chạy lệnh: yarn start xuất hiện lỗi này.

  8. Jack Huynh

    [url=https://postimg.cc/v1CnFXRp][img]https://i.postimg.cc/85zmvnZP/yarn-start.jpg[/img][/url]

  9. Jack Huynh

    Tôi chạy lệnh yarn start xuất hiện lỗi này: https://i.postimg.cc/85zmvnZP/yarn-start.jpg

    Tôi đã chạy lệnh: npm install typescript nhưng vẫn không được. Tôi phải khắc phục lỗi này thế nào ? thank

  10. Hùng

    Em chạy lệnh npm install ở bước 3: Triển khai giao diện thì xuất hiện lỗi này:

    PS C:\Users\HUNGLAPTOP\Desktop\testnet\pancake-swap-interface-v1> npm install
    npm ERR! code ERESOLVE
    npm ERR! ERESOLVE unable to resolve dependency tree
    npm ERR!
    npm ERR! While resolving: @pancakeswap/interface@undefined
    npm ERR! Found: react@17.0.2
    npm ERR! node_modules/react
    npm ERR! react@”^17.0.1″ from the root project
    npm ERR!
    npm ERR! Could not resolve dependency:
    npm ERR! peer react@”^16.8.0″ from @reach/dialog@0.10.5
    npm ERR! node_modules/@reach/dialog
    npm ERR! @reach/dialog@”^0.10.3″ from the root project
    npm ERR!
    npm ERR! Fix the upstream dependency conflict, or retry
    npm ERR! this command with –force, or –legacy-peer-deps
    npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
    npm ERR!
    npm ERR! See C:\Users\HUNGLAPTOP\AppData\Local\npm-cache\eresolve-report.txt for a full report.

    npm ERR! A complete log of this run can be found in:
    npm ERR! C:\Users\HUNGLAPTOP\AppData\Local\npm-cache\_logs\2021-08-10T15_10_36_648Z-debug.log

    Anh có thể xem giúp em lỗi này với anh nhé.

  11. Jame Dhami

    Hi,

    When I build it, I get an error like this. Please help me. Thank

    > build
    > react-scripts build

    Creating an optimized production build…
    Failed to compile.

    src\components\Popups\PopupItem.tsx
    Line 3:27: ‘C:\Users\JAME\Desktop\pancaketestnet\pancake-swap-interface-v1\node_modules\react-spring\web.js’ imported multiple times import/no-duplicates
    Line 5:26: ‘C:\Users\JAME\Desktop\pancaketestnet\pancake-swap-interface-v1\node_modules\react-spring\web.js’ imported multiple times import/no-duplicates

    src\connectors\index.ts
    Line 6:30: Unable to resolve path to module ‘@binance-chain/bsc-connector’ import/no-unresolved

    src\hooks\useAuth.ts
    Line 3:36: Unable to resolve path to module ‘@binance-chain/bsc-connector’ import/no-unresolved

    Search for the keywords to learn more about each error.

    • Sorry you. I haven’t encountered this error yet, so I don’t know how to fix it

    • I was thinking and predict the bug is related to the new version of the library. I suggest you two solutions you can try. I’m not sure if that solves your problem either:
      Solution 1: If you use Windows, try downloading the library from link: node_modules_pancake_v1_win.zip, extract to replace the node_modules folder on your computer. And then you start the application to test.
      Solution 2: Please delete the node_modules folder and the package-lock.json file, then open the package.json file and delete all the ^ characters. Then you reinstall the library with the command:
      npm install
      And then start the application to test.

  12. CAO THIEN

    Link : https://api.pancakeswap.com/api/v1/price hiện không còn truy cập được. Admin giúp mình với

  13. Kuro

    contracts/PancakeRouter.sol:164:1: ParserError: Function, variable, struct or modifier declaration expected.
    interface IPancakeRouter01 {
    ^——-^

    Mình làm theo bạn và bị lỗi này khi compile Pancake Router trên remix, bạn có thể giúp mình sửa lỗi này đc k?

  14. anh

    Bạn ơi viết bài cho Pancake swap v2 đi

  15. Ngoc Lan

    Mình chạy đc rồi ! nhưng add Liquidity xong vào exchange thấy không hiện và không exchanged dược là bị gì nhỉ

  16. cotinh

    Minh gap loi nay:
    node tools/merge-contract.js

    line 20:22 missing ‘;’ at ‘(‘
    line 20:27 missing ‘;’ at ‘(‘
    line 20:37 missing ‘;’ at ‘(‘
    line 20:54 missing ‘;’ at ‘(‘
    line 25:19 extraneous input ”0xbb600ba95884f2c2837114fd2f157d00137e0b65b0fe5226523d720e4a4ce539” expecting {‘,’, ‘)’}
    line 26:13 missing ‘;’ at ‘)’
    line 30:4 extraneous input ‘function’ expecting {, ‘pragma’, ‘import’, ‘abstract’, ‘contract’, ‘interface’, ‘library’, ‘struct’, ‘enum’}
    line 20:22 missing ‘;’ at ‘(‘
    line 20:27 missing ‘;’ at ‘(‘
    line 20:37 missing ‘;’ at ‘(‘
    line 20:54 missing ‘;’ at ‘(‘
    line 25:19 extraneous input ”0xbb600ba95884f2c2837114fd2f157d00137e0b65b0fe5226523d720e4a4ce539” expecting {‘,’, ‘)’}
    line 26:13 missing ‘;’ at ‘)’
    line 30:4 extraneous input ‘function’ expecting {, ‘pragma’, ‘import’, ‘abstract’, ‘contract’, ‘interface’, ‘library’, ‘struct’, ‘enum’}
    Output file: /home/bidd/projects/solidity/Pancaketestnet/pancake-swap-v1-bsc-testnet/pancake-swap-router-v1/build/PancakeRouter01.sol.merge.txt

    contract Factory: 0xF9A04fEAb3248327e9D2E6A89b103b40Eb400708
    Sau đó thử copy file vào remix thì gặp lỗi:
    Expected even of number of hex-nibbled

  17. Trung Vu

    src\components\Popups\PopupItem.tsx
    Line 3:27: ‘C:\Users\Admin\Documents\LIVECOIN.NETWORK\pancake-swap-v1-bsc-testnet-main\pancake-swap-interface-v1\node_modules\react-spring\web.js’ imported multiple times import/no-duplicates
    Line 5:26: ‘C:\Users\Admin\Documents\LIVECOIN.NETWORK\pancake-swap-v1-bsc-testnet-main\pancake-swap-interface-v1\node_modules\react-spring\web.js’ imported multiple times import/no-duplicates

    src\pages\AddLiquidity\index.tsx
    Line 250:9: JSX props should not use functions react/jsx-no-bind

    src\pages\RemoveLiquidity\index.tsx
    Line 385:93: JSX props should not use functions react/jsx-no-bind
    Line 460:17: JSX props should not use functions react/jsx-no-bind
    Line 461:17: JSX props should not use functions react/jsx-no-bind
    Line 642:23: JSX props should not use functions react/jsx-no-bind

    Mình test trên local thì bị lỗi này, fix sao Ad ơi 🙁

  18. Nhiều lỗi mình chưa gặp cũng ko biết fix thế nào. Bạn nào chưa chạy được thử làm theo hướng dẫn này xem nhé:
    https://github.com/nhancv/pancake-swap-testnet#readme

  19. Thanh

    Chào bạn mình muốn thêm cả Cake token và mastercher, timelock thì làm thế nào bạn nhỉ, hướng dẫn mình được không.
    Cám ơn bạn

Trả lời

Giao diện bởi Anders Norén